Skip to content

Commit 595d559

Browse files
authoredAug 19, 2020
Add invertSelf to the DOMMatrix object (#1649)
* Add invertSelf + inverse to the DOMMatrix object * return early for non-invertible + test * made non-invertible return early, with tests
1 parent 5054b7b commit 595d559

File tree

3 files changed

+257
-9
lines changed

3 files changed

+257
-9
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ project adheres to [Semantic Versioning](http://semver.org/).
99
==================
1010
### Changed
1111
### Added
12+
* Added support for `inverse()` and `invertSelf()` to `DOMMatrix` (#1648)
1213
### Fixed
1314
* Fix Pango logging "expect ugly output" on Windows (#1643)
1415

‎lib/DOMMatrix.js

+132-5
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ DOMMatrix.prototype.skewYSelf = function (sy) {
425425
return this
426426
}
427427

428-
DOMMatrix.prototype.flipX = function () {
428+
DOMMatrix.prototype.flipX = function () {
429429
return newInstance(multiply([
430430
-1, 0, 0, 0,
431431
0, 1, 0, 0,
@@ -446,8 +446,135 @@ DOMMatrix.prototype.inverse = function () {
446446
return newInstance(this._values).invertSelf()
447447
}
448448
DOMMatrix.prototype.invertSelf = function () {
449-
// If not invertible, set all attributes to NaN and is2D to false
450-
throw new Error('Not implemented')
449+
var m = this._values;
450+
var inv = m.map(v => 0);
451+
452+
inv[0] = m[5] * m[10] * m[15] -
453+
m[5] * m[11] * m[14] -
454+
m[9] * m[6] * m[15] +
455+
m[9] * m[7] * m[14] +
456+
m[13] * m[6] * m[11] -
457+
m[13] * m[7] * m[10];
458+
459+
inv[4] = -m[4] * m[10] * m[15] +
460+
m[4] * m[11] * m[14] +
461+
m[8] * m[6] * m[15] -
462+
m[8] * m[7] * m[14] -
463+
m[12] * m[6] * m[11] +
464+
m[12] * m[7] * m[10];
465+
466+
inv[8] = m[4] * m[9] * m[15] -
467+
m[4] * m[11] * m[13] -
468+
m[8] * m[5] * m[15] +
469+
m[8] * m[7] * m[13] +
470+
m[12] * m[5] * m[11] -
471+
m[12] * m[7] * m[9];
472+
473+
inv[12] = -m[4] * m[9] * m[14] +
474+
m[4] * m[10] * m[13] +
475+
m[8] * m[5] * m[14] -
476+
m[8] * m[6] * m[13] -
477+
m[12] * m[5] * m[10] +
478+
m[12] * m[6] * m[9];
479+
480+
// If the determinant is zero, this matrix cannot be inverted, and all
481+
// values should be set to NaN, with the is2D flag set to false.
482+
483+
var det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
484+
485+
if (det === 0) {
486+
this._values = m.map(v => NaN);
487+
this._is2D = false;
488+
return this;
489+
}
490+
491+
inv[1] = -m[1] * m[10] * m[15] +
492+
m[1] * m[11] * m[14] +
493+
m[9] * m[2] * m[15] -
494+
m[9] * m[3] * m[14] -
495+
m[13] * m[2] * m[11] +
496+
m[13] * m[3] * m[10];
497+
498+
inv[5] = m[0] * m[10] * m[15] -
499+
m[0] * m[11] * m[14] -
500+
m[8] * m[2] * m[15] +
501+
m[8] * m[3] * m[14] +
502+
m[12] * m[2] * m[11] -
503+
m[12] * m[3] * m[10];
504+
505+
inv[9] = -m[0] * m[9] * m[15] +
506+
m[0] * m[11] * m[13] +
507+
m[8] * m[1] * m[15] -
508+
m[8] * m[3] * m[13] -
509+
m[12] * m[1] * m[11] +
510+
m[12] * m[3] * m[9];
511+
512+
inv[13] = m[0] * m[9] * m[14] -
513+
m[0] * m[10] * m[13] -
514+
m[8] * m[1] * m[14] +
515+
m[8] * m[2] * m[13] +
516+
m[12] * m[1] * m[10] -
517+
m[12] * m[2] * m[9];
518+
519+
inv[2] = m[1] * m[6] * m[15] -
520+
m[1] * m[7] * m[14] -
521+
m[5] * m[2] * m[15] +
522+
m[5] * m[3] * m[14] +
523+
m[13] * m[2] * m[7] -
524+
m[13] * m[3] * m[6];
525+
526+
inv[6] = -m[0] * m[6] * m[15] +
527+
m[0] * m[7] * m[14] +
528+
m[4] * m[2] * m[15] -
529+
m[4] * m[3] * m[14] -
530+
m[12] * m[2] * m[7] +
531+
m[12] * m[3] * m[6];
532+
533+
inv[10] = m[0] * m[5] * m[15] -
534+
m[0] * m[7] * m[13] -
535+
m[4] * m[1] * m[15] +
536+
m[4] * m[3] * m[13] +
537+
m[12] * m[1] * m[7] -
538+
m[12] * m[3] * m[5];
539+
540+
inv[14] = -m[0] * m[5] * m[14] +
541+
m[0] * m[6] * m[13] +
542+
m[4] * m[1] * m[14] -
543+
m[4] * m[2] * m[13] -
544+
m[12] * m[1] * m[6] +
545+
m[12] * m[2] * m[5];
546+
547+
inv[3] = -m[1] * m[6] * m[11] +
548+
m[1] * m[7] * m[10] +
549+
m[5] * m[2] * m[11] -
550+
m[5] * m[3] * m[10] -
551+
m[9] * m[2] * m[7] +
552+
m[9] * m[3] * m[6];
553+
554+
inv[7] = m[0] * m[6] * m[11] -
555+
m[0] * m[7] * m[10] -
556+
m[4] * m[2] * m[11] +
557+
m[4] * m[3] * m[10] +
558+
m[8] * m[2] * m[7] -
559+
m[8] * m[3] * m[6];
560+
561+
inv[11] = -m[0] * m[5] * m[11] +
562+
m[0] * m[7] * m[9] +
563+
m[4] * m[1] * m[11] -
564+
m[4] * m[3] * m[9] -
565+
m[8] * m[1] * m[7] +
566+
m[8] * m[3] * m[5];
567+
568+
inv[15] = m[0] * m[5] * m[10] -
569+
m[0] * m[6] * m[9] -
570+
m[4] * m[1] * m[10] +
571+
m[4] * m[2] * m[9] +
572+
m[8] * m[1] * m[6] -
573+
m[8] * m[2] * m[5];
574+
575+
inv.forEach((v,i) => inv[i] = v/det);
576+
this._values = inv;
577+
return this
451578
}
452579

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

474-
DOMMatrix.prototype.toFloat32Array = function () {
601+
DOMMatrix.prototype.toFloat32Array = function () {
475602
return Float32Array.from(this._values)
476603
}
477604

478-
DOMMatrix.prototype.toFloat64Array = function () {
605+
DOMMatrix.prototype.toFloat64Array = function () {
479606
return this._values.slice(0)
480607
}
481608

‎test/dommatrix.test.js

+124-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function assertApproxDeep(actual, expected, tolerance) {
2323
describe('DOMMatrix', function () {
2424
var Avals = [4,5,1,8, 0,3,6,1, 3,5,0,9, 2,4,6,1]
2525
var Bvals = [1,5,1,0, 0,3,6,1, 3,5,7,2, 2,0,6,1]
26+
var Xvals = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,0]
2627
var AxB = new Float64Array([7,25,31,22, 20,43,24,58, 37,73,45,94, 28,44,8,71])
2728
var BxA = new Float64Array([23,40,89,15, 20,39,66,16, 21,30,87,14, 22,52,74,17])
2829

@@ -374,7 +375,7 @@ describe('DOMMatrix', function () {
374375
})
375376

376377
describe('skewYSelf', function () {})
377-
378+
378379
describe('flipX', function () {
379380
it('works', function () {
380381
var x = new DOMMatrix()
@@ -403,8 +404,127 @@ describe('DOMMatrix', function () {
403404
})
404405
})
405406

406-
describe('inverse', function () {})
407-
describe('invertSelf', function () {})
407+
describe('invertSelf', function () {
408+
it('works for invertible matrices', function() {
409+
var d = new DOMMatrix(Avals)
410+
d.invertSelf()
411+
assertApprox(d.m11, 0.9152542372881356)
412+
assertApprox(d.m12, -0.01694915254237288)
413+
assertApprox(d.m13, -0.7966101694915254)
414+
assertApprox(d.m14, -0.13559322033898305)
415+
assertApprox(d.m21, -1.8305084745762712)
416+
assertApprox(d.m22, -0.9661016949152542)
417+
assertApprox(d.m23, 1.5932203389830508)
418+
assertApprox(d.m24, 1.271186440677966)
419+
assertApprox(d.m31, 0.7966101694915254)
420+
assertApprox(d.m32, 0.559322033898305)
421+
assertApprox(d.m33, -0.711864406779661)
422+
assertApprox(d.m34, -0.5254237288135594)
423+
assertApprox(d.m41, 0.711864406779661)
424+
assertApprox(d.m42, 0.5423728813559322)
425+
assertApprox(d.m43, -0.5084745762711864)
426+
assertApprox(d.m44, -0.6610169491525424)
427+
})
428+
429+
it('works for non-invertible matrices', function() {
430+
var d = new DOMMatrix(Xvals)
431+
d.invertSelf()
432+
assert.strictEqual(isNaN(d.m11), true)
433+
assert.strictEqual(isNaN(d.m12), true)
434+
assert.strictEqual(isNaN(d.m13), true)
435+
assert.strictEqual(isNaN(d.m14), true)
436+
assert.strictEqual(isNaN(d.m21), true)
437+
assert.strictEqual(isNaN(d.m22), true)
438+
assert.strictEqual(isNaN(d.m23), true)
439+
assert.strictEqual(isNaN(d.m24), true)
440+
assert.strictEqual(isNaN(d.m31), true)
441+
assert.strictEqual(isNaN(d.m32), true)
442+
assert.strictEqual(isNaN(d.m33), true)
443+
assert.strictEqual(isNaN(d.m34), true)
444+
assert.strictEqual(isNaN(d.m41), true)
445+
assert.strictEqual(isNaN(d.m42), true)
446+
assert.strictEqual(isNaN(d.m43), true)
447+
assert.strictEqual(isNaN(d.m44), true)
448+
assert.strictEqual(d.is2D, false)
449+
})
450+
})
451+
452+
describe('inverse', function () {
453+
it('preserves the original DOMMatrix', function() {
454+
var d = new DOMMatrix(Avals)
455+
var d2 = d.inverse()
456+
assert.strictEqual(d.m11, Avals[0])
457+
assert.strictEqual(d.m12, Avals[1])
458+
assert.strictEqual(d.m13, Avals[2])
459+
assert.strictEqual(d.m14, Avals[3])
460+
assert.strictEqual(d.m21, Avals[4])
461+
assert.strictEqual(d.m22, Avals[5])
462+
assert.strictEqual(d.m23, Avals[6])
463+
assert.strictEqual(d.m24, Avals[7])
464+
assert.strictEqual(d.m31, Avals[8])
465+
assert.strictEqual(d.m32, Avals[9])
466+
assert.strictEqual(d.m33, Avals[10])
467+
assert.strictEqual(d.m34, Avals[11])
468+
assert.strictEqual(d.m41, Avals[12])
469+
assert.strictEqual(d.m42, Avals[13])
470+
assert.strictEqual(d.m43, Avals[14])
471+
assert.strictEqual(d.m44, Avals[15])
472+
assertApprox(d2.m11, 0.9152542372881356)
473+
assertApprox(d2.m12, -0.01694915254237288)
474+
assertApprox(d2.m13, -0.7966101694915254)
475+
assertApprox(d2.m14, -0.13559322033898305)
476+
assertApprox(d2.m21, -1.8305084745762712)
477+
assertApprox(d2.m22, -0.9661016949152542)
478+
assertApprox(d2.m23, 1.5932203389830508)
479+
assertApprox(d2.m24, 1.271186440677966)
480+
assertApprox(d2.m31, 0.7966101694915254)
481+
assertApprox(d2.m32, 0.559322033898305)
482+
assertApprox(d2.m33, -0.711864406779661)
483+
assertApprox(d2.m34, -0.5254237288135594)
484+
assertApprox(d2.m41, 0.711864406779661)
485+
assertApprox(d2.m42, 0.5423728813559322)
486+
assertApprox(d2.m43, -0.5084745762711864)
487+
assertApprox(d2.m44, -0.6610169491525424)
488+
})
489+
490+
it('preserves the original DOMMatrix for non-invertible matrices', function() {
491+
var d = new DOMMatrix(Xvals)
492+
var d2 = d.inverse()
493+
assert.strictEqual(d.m11, Xvals[0])
494+
assert.strictEqual(d.m12, Xvals[1])
495+
assert.strictEqual(d.m13, Xvals[2])
496+
assert.strictEqual(d.m14, Xvals[3])
497+
assert.strictEqual(d.m21, Xvals[4])
498+
assert.strictEqual(d.m22, Xvals[5])
499+
assert.strictEqual(d.m23, Xvals[6])
500+
assert.strictEqual(d.m24, Xvals[7])
501+
assert.strictEqual(d.m31, Xvals[8])
502+
assert.strictEqual(d.m32, Xvals[9])
503+
assert.strictEqual(d.m33, Xvals[10])
504+
assert.strictEqual(d.m34, Xvals[11])
505+
assert.strictEqual(d.m41, Xvals[12])
506+
assert.strictEqual(d.m42, Xvals[13])
507+
assert.strictEqual(d.m43, Xvals[14])
508+
assert.strictEqual(d.m44, Xvals[15])
509+
assert.strictEqual(isNaN(d2.m11), true)
510+
assert.strictEqual(isNaN(d2.m12), true)
511+
assert.strictEqual(isNaN(d2.m13), true)
512+
assert.strictEqual(isNaN(d2.m14), true)
513+
assert.strictEqual(isNaN(d2.m21), true)
514+
assert.strictEqual(isNaN(d2.m22), true)
515+
assert.strictEqual(isNaN(d2.m23), true)
516+
assert.strictEqual(isNaN(d2.m24), true)
517+
assert.strictEqual(isNaN(d2.m31), true)
518+
assert.strictEqual(isNaN(d2.m32), true)
519+
assert.strictEqual(isNaN(d2.m33), true)
520+
assert.strictEqual(isNaN(d2.m34), true)
521+
assert.strictEqual(isNaN(d2.m41), true)
522+
assert.strictEqual(isNaN(d2.m42), true)
523+
assert.strictEqual(isNaN(d2.m43), true)
524+
assert.strictEqual(isNaN(d2.m44), true)
525+
assert.strictEqual(d2.is2D, false)
526+
})
527+
})
408528

409529
describe('transformPoint', function () {
410530
it('works', function () {
@@ -437,7 +557,7 @@ describe('DOMMatrix', function () {
437557
]))
438558
})
439559
})
440-
560+
441561
describe('toFloat64Array', function () {
442562
it('works', function () {
443563
var x = new DOMMatrix()

0 commit comments

Comments
 (0)
Please sign in to comment.