Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ project adheres to [Semantic Versioning](http://semver.org/).
==================
### Changed
### Added
* Added support for `inverse()` and `invertSelf()` to `DOMMatrix` (#1648)
### Fixed

2.7.0
Expand Down
137 changes: 132 additions & 5 deletions lib/DOMMatrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ DOMMatrix.prototype.skewYSelf = function (sy) {
return this
}

DOMMatrix.prototype.flipX = function () {
DOMMatrix.prototype.flipX = function () {
return newInstance(multiply([
-1, 0, 0, 0,
0, 1, 0, 0,
Expand All @@ -446,8 +446,135 @@ DOMMatrix.prototype.inverse = function () {
return newInstance(this._values).invertSelf()
}
DOMMatrix.prototype.invertSelf = function () {
// If not invertible, set all attributes to NaN and is2D to false
throw new Error('Not implemented')
var m = this._values;
var inv = m.map(v => 0);

inv[0] = m[5] * m[10] * m[15] -
m[5] * m[11] * m[14] -
m[9] * m[6] * m[15] +
m[9] * m[7] * m[14] +
m[13] * m[6] * m[11] -
m[13] * m[7] * m[10];

inv[4] = -m[4] * m[10] * m[15] +
m[4] * m[11] * m[14] +
m[8] * m[6] * m[15] -
m[8] * m[7] * m[14] -
m[12] * m[6] * m[11] +
m[12] * m[7] * m[10];

inv[8] = m[4] * m[9] * m[15] -
m[4] * m[11] * m[13] -
m[8] * m[5] * m[15] +
m[8] * m[7] * m[13] +
m[12] * m[5] * m[11] -
m[12] * m[7] * m[9];

inv[12] = -m[4] * m[9] * m[14] +
m[4] * m[10] * m[13] +
m[8] * m[5] * m[14] -
m[8] * m[6] * m[13] -
m[12] * m[5] * m[10] +
m[12] * m[6] * m[9];

// If the determinant is zero, this matrix cannot be inverted, and all
// values should be set to NaN, with the is2D flag set to false.

var det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];

if (det === 0) {
this._values = m.map(v => NaN);
this._is2D = false;
return this;
}

inv[1] = -m[1] * m[10] * m[15] +
m[1] * m[11] * m[14] +
m[9] * m[2] * m[15] -
m[9] * m[3] * m[14] -
m[13] * m[2] * m[11] +
m[13] * m[3] * m[10];

inv[5] = m[0] * m[10] * m[15] -
m[0] * m[11] * m[14] -
m[8] * m[2] * m[15] +
m[8] * m[3] * m[14] +
m[12] * m[2] * m[11] -
m[12] * m[3] * m[10];

inv[9] = -m[0] * m[9] * m[15] +
m[0] * m[11] * m[13] +
m[8] * m[1] * m[15] -
m[8] * m[3] * m[13] -
m[12] * m[1] * m[11] +
m[12] * m[3] * m[9];

inv[13] = m[0] * m[9] * m[14] -
m[0] * m[10] * m[13] -
m[8] * m[1] * m[14] +
m[8] * m[2] * m[13] +
m[12] * m[1] * m[10] -
m[12] * m[2] * m[9];

inv[2] = m[1] * m[6] * m[15] -
m[1] * m[7] * m[14] -
m[5] * m[2] * m[15] +
m[5] * m[3] * m[14] +
m[13] * m[2] * m[7] -
m[13] * m[3] * m[6];

inv[6] = -m[0] * m[6] * m[15] +
m[0] * m[7] * m[14] +
m[4] * m[2] * m[15] -
m[4] * m[3] * m[14] -
m[12] * m[2] * m[7] +
m[12] * m[3] * m[6];

inv[10] = m[0] * m[5] * m[15] -
m[0] * m[7] * m[13] -
m[4] * m[1] * m[15] +
m[4] * m[3] * m[13] +
m[12] * m[1] * m[7] -
m[12] * m[3] * m[5];

inv[14] = -m[0] * m[5] * m[14] +
m[0] * m[6] * m[13] +
m[4] * m[1] * m[14] -
m[4] * m[2] * m[13] -
m[12] * m[1] * m[6] +
m[12] * m[2] * m[5];

inv[3] = -m[1] * m[6] * m[11] +
m[1] * m[7] * m[10] +
m[5] * m[2] * m[11] -
m[5] * m[3] * m[10] -
m[9] * m[2] * m[7] +
m[9] * m[3] * m[6];

inv[7] = m[0] * m[6] * m[11] -
m[0] * m[7] * m[10] -
m[4] * m[2] * m[11] +
m[4] * m[3] * m[10] +
m[8] * m[2] * m[7] -
m[8] * m[3] * m[6];

inv[11] = -m[0] * m[5] * m[11] +
m[0] * m[7] * m[9] +
m[4] * m[1] * m[11] -
m[4] * m[3] * m[9] -
m[8] * m[1] * m[7] +
m[8] * m[3] * m[5];

inv[15] = m[0] * m[5] * m[10] -
m[0] * m[6] * m[9] -
m[4] * m[1] * m[10] +
m[4] * m[2] * m[9] +
m[8] * m[1] * m[6] -
m[8] * m[2] * m[5];

inv.forEach((v,i) => inv[i] = v/det);
this._values = inv;
return this
}

DOMMatrix.prototype.setMatrixValue = function (transformList) {
Expand All @@ -471,11 +598,11 @@ DOMMatrix.prototype.transformPoint = function (point) {
return new DOMPoint(nx, ny, nz, nw)
}

DOMMatrix.prototype.toFloat32Array = function () {
DOMMatrix.prototype.toFloat32Array = function () {
return Float32Array.from(this._values)
}

DOMMatrix.prototype.toFloat64Array = function () {
DOMMatrix.prototype.toFloat64Array = function () {
return this._values.slice(0)
}

Expand Down
128 changes: 124 additions & 4 deletions test/dommatrix.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function assertApproxDeep(actual, expected, tolerance) {
describe('DOMMatrix', function () {
var Avals = [4,5,1,8, 0,3,6,1, 3,5,0,9, 2,4,6,1]
var Bvals = [1,5,1,0, 0,3,6,1, 3,5,7,2, 2,0,6,1]
var Xvals = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,0]
var AxB = new Float64Array([7,25,31,22, 20,43,24,58, 37,73,45,94, 28,44,8,71])
var BxA = new Float64Array([23,40,89,15, 20,39,66,16, 21,30,87,14, 22,52,74,17])

Expand Down Expand Up @@ -374,7 +375,7 @@ describe('DOMMatrix', function () {
})

describe('skewYSelf', function () {})

describe('flipX', function () {
it('works', function () {
var x = new DOMMatrix()
Expand Down Expand Up @@ -403,8 +404,127 @@ describe('DOMMatrix', function () {
})
})

describe('inverse', function () {})
describe('invertSelf', function () {})
describe('invertSelf', function () {
it('works for invertible matrices', function() {
var d = new DOMMatrix(Avals)
d.invertSelf()
assertApprox(d.m11, 0.9152542372881356)
assertApprox(d.m12, -0.01694915254237288)
assertApprox(d.m13, -0.7966101694915254)
assertApprox(d.m14, -0.13559322033898305)
assertApprox(d.m21, -1.8305084745762712)
assertApprox(d.m22, -0.9661016949152542)
assertApprox(d.m23, 1.5932203389830508)
assertApprox(d.m24, 1.271186440677966)
assertApprox(d.m31, 0.7966101694915254)
assertApprox(d.m32, 0.559322033898305)
assertApprox(d.m33, -0.711864406779661)
assertApprox(d.m34, -0.5254237288135594)
assertApprox(d.m41, 0.711864406779661)
assertApprox(d.m42, 0.5423728813559322)
assertApprox(d.m43, -0.5084745762711864)
assertApprox(d.m44, -0.6610169491525424)
})

it('works for non-invertible matrices', function() {
var d = new DOMMatrix(Xvals)
d.invertSelf()
assert.strictEqual(isNaN(d.m11), true)
assert.strictEqual(isNaN(d.m12), true)
assert.strictEqual(isNaN(d.m13), true)
assert.strictEqual(isNaN(d.m14), true)
assert.strictEqual(isNaN(d.m21), true)
assert.strictEqual(isNaN(d.m22), true)
assert.strictEqual(isNaN(d.m23), true)
assert.strictEqual(isNaN(d.m24), true)
assert.strictEqual(isNaN(d.m31), true)
assert.strictEqual(isNaN(d.m32), true)
assert.strictEqual(isNaN(d.m33), true)
assert.strictEqual(isNaN(d.m34), true)
assert.strictEqual(isNaN(d.m41), true)
assert.strictEqual(isNaN(d.m42), true)
assert.strictEqual(isNaN(d.m43), true)
assert.strictEqual(isNaN(d.m44), true)
assert.strictEqual(d.is2D, false)
})
})

describe('inverse', function () {
it('preserves the original DOMMatrix', function() {
var d = new DOMMatrix(Avals)
var d2 = d.inverse()
assert.strictEqual(d.m11, Avals[0])
assert.strictEqual(d.m12, Avals[1])
assert.strictEqual(d.m13, Avals[2])
assert.strictEqual(d.m14, Avals[3])
assert.strictEqual(d.m21, Avals[4])
assert.strictEqual(d.m22, Avals[5])
assert.strictEqual(d.m23, Avals[6])
assert.strictEqual(d.m24, Avals[7])
assert.strictEqual(d.m31, Avals[8])
assert.strictEqual(d.m32, Avals[9])
assert.strictEqual(d.m33, Avals[10])
assert.strictEqual(d.m34, Avals[11])
assert.strictEqual(d.m41, Avals[12])
assert.strictEqual(d.m42, Avals[13])
assert.strictEqual(d.m43, Avals[14])
assert.strictEqual(d.m44, Avals[15])
assertApprox(d2.m11, 0.9152542372881356)
assertApprox(d2.m12, -0.01694915254237288)
assertApprox(d2.m13, -0.7966101694915254)
assertApprox(d2.m14, -0.13559322033898305)
assertApprox(d2.m21, -1.8305084745762712)
assertApprox(d2.m22, -0.9661016949152542)
assertApprox(d2.m23, 1.5932203389830508)
assertApprox(d2.m24, 1.271186440677966)
assertApprox(d2.m31, 0.7966101694915254)
assertApprox(d2.m32, 0.559322033898305)
assertApprox(d2.m33, -0.711864406779661)
assertApprox(d2.m34, -0.5254237288135594)
assertApprox(d2.m41, 0.711864406779661)
assertApprox(d2.m42, 0.5423728813559322)
assertApprox(d2.m43, -0.5084745762711864)
assertApprox(d2.m44, -0.6610169491525424)
})

it('preserves the original DOMMatrix for non-invertible matrices', function() {
var d = new DOMMatrix(Xvals)
var d2 = d.inverse()
assert.strictEqual(d.m11, Xvals[0])
assert.strictEqual(d.m12, Xvals[1])
assert.strictEqual(d.m13, Xvals[2])
assert.strictEqual(d.m14, Xvals[3])
assert.strictEqual(d.m21, Xvals[4])
assert.strictEqual(d.m22, Xvals[5])
assert.strictEqual(d.m23, Xvals[6])
assert.strictEqual(d.m24, Xvals[7])
assert.strictEqual(d.m31, Xvals[8])
assert.strictEqual(d.m32, Xvals[9])
assert.strictEqual(d.m33, Xvals[10])
assert.strictEqual(d.m34, Xvals[11])
assert.strictEqual(d.m41, Xvals[12])
assert.strictEqual(d.m42, Xvals[13])
assert.strictEqual(d.m43, Xvals[14])
assert.strictEqual(d.m44, Xvals[15])
assert.strictEqual(isNaN(d2.m11), true)
assert.strictEqual(isNaN(d2.m12), true)
assert.strictEqual(isNaN(d2.m13), true)
assert.strictEqual(isNaN(d2.m14), true)
assert.strictEqual(isNaN(d2.m21), true)
assert.strictEqual(isNaN(d2.m22), true)
assert.strictEqual(isNaN(d2.m23), true)
assert.strictEqual(isNaN(d2.m24), true)
assert.strictEqual(isNaN(d2.m31), true)
assert.strictEqual(isNaN(d2.m32), true)
assert.strictEqual(isNaN(d2.m33), true)
assert.strictEqual(isNaN(d2.m34), true)
assert.strictEqual(isNaN(d2.m41), true)
assert.strictEqual(isNaN(d2.m42), true)
assert.strictEqual(isNaN(d2.m43), true)
assert.strictEqual(isNaN(d2.m44), true)
assert.strictEqual(d2.is2D, false)
})
})

describe('transformPoint', function () {
it('works', function () {
Expand Down Expand Up @@ -437,7 +557,7 @@ describe('DOMMatrix', function () {
]))
})
})

describe('toFloat64Array', function () {
it('works', function () {
var x = new DOMMatrix()
Expand Down