Skip to content

Commit eff21db

Browse files
committed
inverse()
1 parent b13ea5d commit eff21db

2 files changed

Lines changed: 359 additions & 0 deletions

File tree

src/main/java/org/htmlunit/javascript/host/dom/DOMMatrixReadOnly.java

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,4 +437,234 @@ public DOMMatrixReadOnly flipY() {
437437
matrix.is2D_ = this.is2D_;
438438
return matrix;
439439
}
440+
441+
/**
442+
* @return new matrix which is the inverse of the original matrix.
443+
* If the matrix cannot be inverted, the new matrix's components are all set to NaN
444+
* and its is2D property is set to false. The original matrix is not changed.
445+
*/
446+
@JsxFunction
447+
public DOMMatrixReadOnly inverse() {
448+
final DOMMatrixReadOnly matrix = new DOMMatrixReadOnly();
449+
final Window window = getWindow();
450+
matrix.setParentScope(window);
451+
matrix.setPrototype(window.getPrototype(DOMMatrixReadOnly.class));
452+
453+
if (is2D_) {
454+
final double det = m11_ * m22_ - m12_ * m21_;
455+
if (det == 0) {
456+
// Not invertible: set all to NaN, is2D_ = false
457+
matrix.m11_ = Double.NaN;
458+
matrix.m12_ = Double.NaN;
459+
matrix.m13_ = Double.NaN;
460+
matrix.m14_ = Double.NaN;
461+
matrix.m21_ = Double.NaN;
462+
matrix.m22_ = Double.NaN;
463+
matrix.m23_ = Double.NaN;
464+
matrix.m24_ = Double.NaN;
465+
matrix.m31_ = Double.NaN;
466+
matrix.m32_ = Double.NaN;
467+
matrix.m33_ = Double.NaN;
468+
matrix.m34_ = Double.NaN;
469+
matrix.m41_ = Double.NaN;
470+
matrix.m42_ = Double.NaN;
471+
matrix.m43_ = Double.NaN;
472+
matrix.m44_ = Double.NaN;
473+
matrix.is2D_ = false;
474+
return matrix;
475+
}
476+
477+
matrix.m11_ = m22_ / det;
478+
matrix.m12_ = -m12_ / det;
479+
matrix.m13_ = 0;
480+
matrix.m14_ = 0;
481+
482+
matrix.m21_ = -m21_ / det;
483+
matrix.m22_ = m11_ / det;
484+
matrix.m23_ = 0;
485+
matrix.m24_ = 0;
486+
487+
matrix.m31_ = 0;
488+
matrix.m32_ = 0;
489+
matrix.m33_ = 1;
490+
matrix.m34_ = 0;
491+
492+
matrix.m41_ = (m21_ * m42_ - m22_ * m41_) / det;
493+
matrix.m42_ = (m12_ * m41_ - m11_ * m42_) / det;
494+
matrix.m43_ = 0;
495+
matrix.m44_ = 1;
496+
497+
matrix.is2D_ = true;
498+
return matrix;
499+
}
500+
501+
// 3D/4x4 matrix inversion
502+
final double[] m = {
503+
m11_, m12_, m13_, m14_,
504+
m21_, m22_, m23_, m24_,
505+
m31_, m32_, m33_, m34_,
506+
m41_, m42_, m43_, m44_
507+
};
508+
509+
final double[] inv = new double[16];
510+
inv[0] = m[5] * m[10] * m[15]
511+
- m[5] * m[11] * m[14]
512+
- m[9] * m[6] * m[15]
513+
+ m[9] * m[7] * m[14]
514+
+ m[13] * m[6] * m[11]
515+
- m[13] * m[7] * m[10];
516+
517+
inv[4] = -m[4] * m[10] * m[15]
518+
+ m[4] * m[11] * m[14]
519+
+ m[8] * m[6] * m[15]
520+
- m[8] * m[7] * m[14]
521+
- m[12] * m[6] * m[11]
522+
+ m[12] * m[7] * m[10];
523+
524+
inv[8] = m[4] * m[9] * m[15]
525+
- m[4] * m[11] * m[13]
526+
- m[8] * m[5] * m[15]
527+
+ m[8] * m[7] * m[13]
528+
+ m[12] * m[5] * m[11]
529+
- m[12] * m[7] * m[9];
530+
531+
inv[12] = -m[4] * m[9] * m[14]
532+
+ m[4] * m[10] * m[13]
533+
+ m[8] * m[5] * m[14]
534+
- m[8] * m[6] * m[13]
535+
- m[12] * m[5] * m[10]
536+
+ m[12] * m[6] * m[9];
537+
538+
inv[1] = -m[1] * m[10] * m[15]
539+
+ m[1] * m[11] * m[14]
540+
+ m[9] * m[2] * m[15]
541+
- m[9] * m[3] * m[14]
542+
- m[13] * m[2] * m[11]
543+
+ m[13] * m[3] * m[10];
544+
545+
inv[5] = m[0] * m[10] * m[15]
546+
- m[0] * m[11] * m[14]
547+
- m[8] * m[2] * m[15]
548+
+ m[8] * m[3] * m[14]
549+
+ m[12] * m[2] * m[11]
550+
- m[12] * m[3] * m[10];
551+
552+
inv[9] = -m[0] * m[9] * m[15]
553+
+ m[0] * m[11] * m[13]
554+
+ m[8] * m[1] * m[15]
555+
- m[8] * m[3] * m[13]
556+
- m[12] * m[1] * m[11]
557+
+ m[12] * m[3] * m[9];
558+
559+
inv[13] = m[0] * m[9] * m[14]
560+
- m[0] * m[10] * m[13]
561+
- m[8] * m[1] * m[14]
562+
+ m[8] * m[2] * m[13]
563+
+ m[12] * m[1] * m[10]
564+
- m[12] * m[2] * m[9];
565+
566+
inv[2] = m[1] * m[6] * m[15]
567+
- m[1] * m[7] * m[14]
568+
- m[5] * m[2] * m[15]
569+
+ m[5] * m[3] * m[14]
570+
+ m[13] * m[2] * m[7]
571+
- m[13] * m[3] * m[6];
572+
573+
inv[6] = -m[0] * m[6] * m[15]
574+
+ m[0] * m[7] * m[14]
575+
+ m[4] * m[2] * m[15]
576+
- m[4] * m[3] * m[14]
577+
- m[12] * m[2] * m[7]
578+
+ m[12] * m[3] * m[6];
579+
580+
inv[10] = m[0] * m[5] * m[15]
581+
- m[0] * m[7] * m[13]
582+
- m[4] * m[1] * m[15]
583+
+ m[4] * m[3] * m[13]
584+
+ m[12] * m[1] * m[7]
585+
- m[12] * m[3] * m[5];
586+
587+
inv[14] = -m[0] * m[5] * m[14]
588+
+ m[0] * m[6] * m[13]
589+
+ m[4] * m[1] * m[14]
590+
- m[4] * m[2] * m[13]
591+
- m[12] * m[1] * m[6]
592+
+ m[12] * m[2] * m[5];
593+
594+
inv[3] = -m[1] * m[6] * m[11]
595+
+ m[1] * m[7] * m[10]
596+
+ m[5] * m[2] * m[11]
597+
- m[5] * m[3] * m[10]
598+
- m[9] * m[2] * m[7]
599+
+ m[9] * m[3] * m[6];
600+
601+
inv[7] = m[0] * m[6] * m[11]
602+
- m[0] * m[7] * m[10]
603+
- m[4] * m[2] * m[11]
604+
+ m[4] * m[3] * m[10]
605+
+ m[8] * m[2] * m[7]
606+
- m[8] * m[3] * m[6];
607+
608+
inv[11] = -m[0] * m[5] * m[11]
609+
+ m[0] * m[7] * m[9]
610+
+ m[4] * m[1] * m[11]
611+
- m[4] * m[3] * m[9]
612+
- m[8] * m[1] * m[7]
613+
+ m[8] * m[3] * m[5];
614+
615+
inv[15] = m[0] * m[5] * m[10]
616+
- m[0] * m[6] * m[9]
617+
- m[4] * m[1] * m[10]
618+
+ m[4] * m[2] * m[9]
619+
+ m[8] * m[1] * m[6]
620+
- m[8] * m[2] * m[5];
621+
622+
double det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
623+
624+
if (det == 0) {
625+
// Not invertible: set all to NaN, is2D_ = false
626+
matrix.m11_ = Double.NaN;
627+
matrix.m12_ = Double.NaN;
628+
matrix.m13_ = Double.NaN;
629+
matrix.m14_ = Double.NaN;
630+
matrix.m21_ = Double.NaN;
631+
matrix.m22_ = Double.NaN;
632+
matrix.m23_ = Double.NaN;
633+
matrix.m24_ = Double.NaN;
634+
matrix.m31_ = Double.NaN;
635+
matrix.m32_ = Double.NaN;
636+
matrix.m33_ = Double.NaN;
637+
matrix.m34_ = Double.NaN;
638+
matrix.m41_ = Double.NaN;
639+
matrix.m42_ = Double.NaN;
640+
matrix.m43_ = Double.NaN;
641+
matrix.m44_ = Double.NaN;
642+
matrix.is2D_ = false;
643+
return matrix;
644+
}
645+
646+
det = 1.0 / det;
647+
648+
matrix.m11_ = inv[0] * det;
649+
matrix.m12_ = inv[1] * det;
650+
matrix.m13_ = inv[2] * det;
651+
matrix.m14_ = inv[3] * det;
652+
653+
matrix.m21_ = inv[4] * det;
654+
matrix.m22_ = inv[5] * det;
655+
matrix.m23_ = inv[6] * det;
656+
matrix.m24_ = inv[7] * det;
657+
658+
matrix.m31_ = inv[8] * det;
659+
matrix.m32_ = inv[9] * det;
660+
matrix.m33_ = inv[10] * det;
661+
matrix.m34_ = inv[11] * det;
662+
663+
matrix.m41_ = inv[12] * det;
664+
matrix.m42_ = inv[13] * det;
665+
matrix.m43_ = inv[14] * det;
666+
matrix.m44_ = inv[15] * det;
667+
668+
return matrix;
669+
}
440670
}

src/test/java/org/htmlunit/javascript/host/dom/DOMMatrixReadOnlyTest.java

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,4 +523,133 @@ public void flipY_3D() throws Exception {
523523

524524
loadPageVerifyTitle2(html);
525525
}
526+
/**
527+
* @throws Exception on test failure
528+
*/
529+
@Test
530+
@Alerts({"[1, 0, 0, 1, 0, 0]", "true"})
531+
public void inverse_identity2D() throws Exception {
532+
final String html = DOCTYPE_HTML
533+
+ "<html><body><script>"
534+
+ LOG_TITLE_FUNCTION
535+
+ DUMP_2D_FUNCTION
536+
+ "let m = new DOMMatrixReadOnly();"
537+
+ "let inv = m.inverse();"
538+
+ "dump(inv);"
539+
+ "</script></body></html>";
540+
loadPageVerifyTitle2(html);
541+
}
542+
543+
/**
544+
* @throws Exception on test failure
545+
*/
546+
@Test
547+
@Alerts({"[0.25, 0, 0, 0.2, -2.5, -2.6]", "true",
548+
"[4, 0, 0, 5, 10, 13]", "true"})
549+
public void inverse_general2D() throws Exception {
550+
final String html = DOCTYPE_HTML
551+
+ "<html><body><script>"
552+
+ LOG_TITLE_FUNCTION
553+
+ DUMP_2D_FUNCTION
554+
+ "let m = new DOMMatrixReadOnly([4, 0, 0, 5, 10, 13]);"
555+
+ "let inv = m.inverse();"
556+
+ "dump(inv);"
557+
+ "dump(m);"
558+
+ "</script></body></html>";
559+
loadPageVerifyTitle2(html);
560+
}
561+
562+
/**
563+
* @throws Exception on test failure
564+
*/
565+
@Test
566+
@Alerts({"[NaN, NaN, NaN, NaN, NaN, NaN]", "false"})
567+
public void inverse_singular2D() throws Exception {
568+
final String html = DOCTYPE_HTML
569+
+ "<html><body><script>"
570+
+ LOG_TITLE_FUNCTION
571+
+ DUMP_2D_FUNCTION
572+
+ "try {"
573+
+ " let m = new DOMMatrixReadOnly([0, 0, 0, 0, 0, 0]);"
574+
+ " let inv = m.inverse();"
575+
+ " dump(inv);"
576+
+ "} catch(e) { logEx(e); }"
577+
+ "</script></body></html>";
578+
loadPageVerifyTitle2(html);
579+
}
580+
581+
/**
582+
* @throws Exception on test failure
583+
*/
584+
@Test
585+
@Alerts({"[1, 0, 0, 1, 0, 0]",
586+
"1[1, 0, 0, 0]",
587+
"2[0, 1, 0, 0]",
588+
"3[0, 0, 1, 0]",
589+
"4[0, 0, 0, 1]",
590+
"false"})
591+
public void inverse_identity3D() throws Exception {
592+
final String html = DOCTYPE_HTML
593+
+ "<html><body><script>"
594+
+ LOG_TITLE_FUNCTION
595+
+ DUMP_FUNCTION
596+
+ "let m = new DOMMatrixReadOnly([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);"
597+
+ "let inv = m.inverse();"
598+
+ "dump(inv);"
599+
+ "</script></body></html>";
600+
loadPageVerifyTitle2(html);
601+
}
602+
603+
/**
604+
* @throws Exception on test failure
605+
*/
606+
@Test
607+
@Alerts({"[NaN, NaN, NaN, NaN, NaN, NaN]",
608+
"1[NaN, NaN, NaN, NaN]",
609+
"2[NaN, NaN, NaN, NaN]",
610+
"3[NaN, NaN, NaN, NaN]",
611+
"4[NaN, NaN, NaN, NaN]",
612+
"false"})
613+
public void inverse_singular3D() throws Exception {
614+
final String html = DOCTYPE_HTML
615+
+ "<html><body><script>"
616+
+ LOG_TITLE_FUNCTION
617+
+ DUMP_FUNCTION
618+
+ "try {"
619+
+ " let m = new DOMMatrixReadOnly([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);"
620+
+ " let inv = m.inverse();"
621+
+ " dump(inv);"
622+
+ "} catch(e) { logEx(e); }"
623+
+ "</script></body></html>";
624+
loadPageVerifyTitle2(html);
625+
}
626+
627+
/**
628+
* @throws Exception on test failure
629+
*/
630+
@Test
631+
@Alerts({"[2, 0, 0, 0.5, 0, 0]",
632+
"1[2, 0, 0, 0]",
633+
"2[0, 0.5, 0, 0]",
634+
"3[0, 0, 0.25, 0]",
635+
"4[0, 0, 0, 1]",
636+
"false",
637+
"[0.5, 0, 0, 2, 0, 0]",
638+
"1[0.5, 0, 0, 0]",
639+
"2[0, 2, 0, 0]",
640+
"3[0, 0, 4, 0]",
641+
"4[0, 0, 0, 1]",
642+
"false"})
643+
public void inverse_general3D() throws Exception {
644+
final String html = DOCTYPE_HTML
645+
+ "<html><body><script>"
646+
+ LOG_TITLE_FUNCTION
647+
+ DUMP_FUNCTION
648+
+ "let m = new DOMMatrixReadOnly([0.5,0,0,0,0,2,0,0,0,0,4,0,0,0,0,1]);"
649+
+ "let inv = m.inverse();"
650+
+ "dump(inv);"
651+
+ "dump(m);"
652+
+ "</script></body></html>";
653+
loadPageVerifyTitle2(html);
654+
}
526655
}

0 commit comments

Comments
 (0)