tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_DOMMatrix.html (21124B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <title>Test DOMMatrix behavior</title>
      5  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      6  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
      7 </head>
      8 <script>
      9 function createMatrix(a, b, c, d, e, f)
     10 {
     11  var m = new DOMMatrix();
     12  m.a = a;
     13  m.b = b;
     14  m.c = c;
     15  m.d = d;
     16  m.e = e;
     17  m.f = f;
     18  return m;
     19 }
     20 
     21 function create3DMatrix(a, b, c, d, e, f)
     22 {
     23  var m = new DOMMatrix();
     24  m.a = a;
     25  m.b = b;
     26  m.c = c;
     27  m.d = d;
     28  m.e = e;
     29  m.f = f;
     30  m.m13 = 0;
     31  return m;
     32 }
     33 
     34 function cmpMatrix(a, b, msg)
     35 {
     36  if (Array.isArray(a))
     37    a = new DOMMatrix(a);
     38  if (Array.isArray(b))
     39    b = new DOMMatrix(b);
     40 
     41  ok(CompareDOMMatrix(a, b),
     42     msg + " - got " + formatMatrix(a)
     43         + ", expected " + formatMatrix(b));
     44 }
     45 
     46 function roughCmpMatrix(a, b, msg)
     47 {
     48  if (Array.isArray(a))
     49    a = new DOMMatrix(a);
     50  if (Array.isArray(b))
     51    b = new DOMMatrix(b);
     52 
     53  ok(RoughCompareDOMMatrix(a, b),
     54     msg + " - got " + formatMatrix(a)
     55         + ", expected " + formatMatrix(b));
     56 }
     57 
     58 function formatMatrix(m)
     59 {
     60  m = new DOMMatrix(m);
     61 
     62  if (m.is2D)
     63    return "(" + [m.a, m.b, m.c, m.d, m.e, m.f].join(', ') + ")";
     64  else
     65    return "(" + [m.m11, m.m12, m.m13, m.m14,
     66                  m.m21, m.m22, m.m23, m.m24,
     67                  m.m31, m.m32, m.m33, m.m34,
     68                  m.m41, m.m42, m.m43, m.m44,].join(', ') + ")";
     69 }
     70 
     71 function CompareMatrix(dm, m)
     72 {
     73  var ma = m.toFloat32Array();
     74  for (var x = 0; x < ma.length; x++) {
     75    if (Math.abs(ma[x] - dm.m[x]) > 0.000001)
     76      return false;
     77  }
     78 
     79  return true;
     80 }
     81 
     82 function CompareDOMMatrix(dm1, dm2)
     83 {
     84  var m1 = dm1.toFloat32Array();
     85  var m2 = dm2.toFloat32Array();
     86 
     87  if (m1.length !=  m2.length)
     88    return false;
     89 
     90  for (var x = 0; x < m1.length; x++) {
     91    if (Math.abs(m1[x] - m2[x]) > 0.000001)
     92      return false;
     93  }
     94 
     95  return true;
     96 }
     97 
     98 function RoughCompareDOMMatrix(dm1, dm2)
     99 {
    100  var m1 = dm1.toFloat32Array();
    101  var m2 = dm2.toFloat32Array();
    102 
    103  if (m1.length != m2.length)
    104    return false;
    105 
    106  const tolerance = 1 / 65535;
    107  for (var x = 0; x < m1.length; x++) {
    108    if (Math.abs(m1[x] - m2[x]) > tolerance)
    109      return false;
    110  }
    111 
    112  return true;
    113 }
    114 
    115 SimpleTest.waitForExplicitFinish();
    116 
    117 function main()
    118 {
    119  var tests = [
    120      testCreateMatrix,
    121      testMultiply,
    122      testInverse,
    123      testTranslate,
    124      testScale,
    125      testScaleNonUniform,
    126      testRotate,
    127      testRotateFromVector,
    128      testFlipX,
    129      testFlipY,
    130      testSkewX,
    131      testSkewY,
    132      testMultiplyInPlace,
    133      testInverseInPlace,
    134      testTranslateInPlace,
    135      testScaleInPlace,
    136      testRotateInPlace,
    137      testRotateFromVectorInPlace,
    138      testSkewXInPlace,
    139      testSkewYInPlace,
    140      testCreateMatrix3D,
    141      testMultiply3D,
    142      testInverse3D,
    143      testTranslate3D,
    144      testScale3D,
    145      test3D,
    146      testParsing,
    147      testStringify
    148    ];
    149  for (var i = 0; i < tests.length; i++) {
    150    try{
    151      tests[i]();
    152    } catch (e) {
    153      ok(false, "uncaught exception in test " + i + ": " + e.name);
    154    }
    155  }
    156  SimpleTest.finish();
    157 }
    158 
    159 function testCreateMatrix()
    160 {
    161  var m = new DOMMatrix();
    162 
    163  // Should be initialised to identity
    164  cmpMatrix(m, [1, 0, 0, 1, 0, 0],
    165            "DOMMatrix should produce identity matrix");
    166 
    167  m = new DOMMatrix([1,2,3,4,5,6]);
    168  cmpMatrix(m, [1,2,3,4,5,6],
    169            "DOMMatrix should produce the same matrix");
    170  ok(m.is2D, "Failed to mark matrix as 2D.");
    171 
    172  m = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
    173  cmpMatrix(m, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
    174            "DOMMatrix should produce the same matrix");
    175  ok(!m.is2D, "Failed to mark matrix as 3D.");
    176 
    177  var n = new DOMMatrix(m.toFloat32Array());
    178  cmpMatrix(n, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
    179            "DOMMatrix should produce the same matrix with float32array constructor");
    180  ok(!n.is2D, "Failed to mark matrix as 3D.");
    181 
    182  var n = new DOMMatrix(m.toFloat64Array());
    183  cmpMatrix(n, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
    184            "DOMMatrix should produce the same matrix with float64array constructor");
    185  ok(!n.is2D, "Failed to mark matrix as 3D.");
    186 
    187  var exn = null;
    188  try {
    189    m = new DOMMatrix([0]);
    190  } catch (e) {
    191    exn = e;
    192  }
    193  ok(exn, "did throw exception with bad DOMMatrix constructor with 1 parameter");
    194 
    195  exn = null;
    196  try {
    197    m = new DOMMatrix([1,2,3,4,5,6,7,8,9]);
    198  } catch (e) {
    199    exn = e;
    200  }
    201  ok(exn, "did throw exception with bad DOMMatrix constructor with 9 parameters");
    202 
    203  exn = null;
    204  try {
    205    m = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]);
    206  } catch (e) {
    207    exn = e;
    208  }
    209  ok(exn, "did throw exception with bad DOMMatrix constructor with 17 parameters");
    210 }
    211 
    212 // DOMMatrix multiply(in DOMMatrix secondMatrix);
    213 function testMultiply()
    214 {
    215  var m1 = createMatrix(1, 0, 0, 1, 50, 90);
    216  var m2 = createMatrix(Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0);
    217  var m3 = createMatrix(1, 0, 0, 1, 130, 160);
    218  var result = m1.multiply(m2).multiply(m3);
    219  roughCmpMatrix(result, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 255.060974, 111.213203],
    220    "Unexpected result after multiplying matrices");
    221 
    222  // Check orig matrices are unchanged
    223  cmpMatrix(m1, [1, 0, 0, 1, 50, 90], "Matrix changed after multiplication");
    224  roughCmpMatrix(m2, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0],
    225                 "Matrix changed after multiplication");
    226  cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication");
    227 }
    228 
    229 // DOMMatrix inverse() raises(SVGException);
    230 function testInverse()
    231 {
    232  // Test inversion
    233  var m = createMatrix(2, 0, 0, 4, 110, -50);
    234  roughCmpMatrix(m.inverse(), [0.5, 0, 0, 0.25, -55, 12.5],
    235    "Unexpected result after inverting matrix");
    236 
    237  // Test non-invertable
    238  m = createMatrix(0, 0, 1, 0, 0, 0);
    239  m = m.inverse();
    240  ok(isNaN(m.a), "Failed to invalidate inverted singular matrix, got " + m.a);
    241  ok(!m.is2D, "Failed to mark invalidated inverted singular matrix as 3D.");
    242 }
    243 
    244 // DOMMatrix translate(in float x, in float y);
    245 function testTranslate()
    246 {
    247  var m = createMatrix(2, 0, 0, 1, 120, 100);
    248  roughCmpMatrix(m.translate(100, -50), [2, 0, 0, 1, 320, 50],
    249    "Unexpected result after translate");
    250 }
    251 
    252 // DOMMatrix scale(in float scaleFactor);
    253 function testScale()
    254 {
    255  var m = createMatrix(2, 0, 0, 1, 120, 100);
    256  roughCmpMatrix(m.scale(0.5), [1, 0, 0, 0.5, 120, 100],
    257    "Unexpected result after scale");
    258 }
    259 
    260 // DOMMatrix scaleNonUniform(in float scaleFactorX, in float scaleFactorY);
    261 function testScaleNonUniform()
    262 {
    263  var m = createMatrix(2, 0, 0, 1, 120, 100);
    264  roughCmpMatrix(m.scaleNonUniform(0.5, -3), [1, 0, 0, -3, 120, 100],
    265    "Unexpected result after scaleNonUniform");
    266 }
    267 
    268 // DOMMatrix rotate(in float angle);
    269 function testRotate()
    270 {
    271  var m = createMatrix(2, 0, 0, 1, 120, 100);
    272  roughCmpMatrix(m.rotate(45),
    273                 [2*Math.cos(Math.PI/4), Math.sin(Math.PI/4),
    274                  2*-Math.sin(Math.PI/4), Math.cos(Math.PI/4),
    275                  120, 100],
    276                 "Unexpected result after rotate");
    277 }
    278 
    279 // DOMMatrix rotateFromVector(in float x, in float y) raises(SVGException);
    280 function testRotateFromVector()
    281 {
    282  var m = createMatrix(2, 0, 0, 1, 120, 100);
    283  // Make a 150 degree angle
    284  var result = m.rotateFromVector(-2, 1.1547);
    285  roughCmpMatrix(result,
    286                 [2*Math.cos(5*Math.PI/6), Math.sin(5*Math.PI/6),
    287                  2*-Math.sin(5*Math.PI/6), Math.cos(5*Math.PI/6),
    288                  120, 100],
    289                 "Unexpected result after rotateFromVector");
    290 
    291  // Test bad input (1)
    292  var exn = null;
    293  try {
    294    m.rotateFromVector(1, 0);
    295  } catch (e) {
    296    exn = e;
    297  }
    298  is(exn, null, "did not throw exception with zero coord for rotateFromVector");
    299 
    300  // Test bad input (2)
    301  exn = null;
    302  try {
    303    m.rotateFromVector(0, 1);
    304  } catch (e) {
    305    exn = e;
    306  }
    307  is(exn, null, "did not throw exception with zero coord for rotateFromVector");
    308 }
    309 
    310 // DOMMatrix flipX();
    311 function testFlipX()
    312 {
    313  var m = createMatrix(1, 2, 3, 4, 5, 6);
    314  cmpMatrix(m.flipX(), [-1, -2, 3, 4, 5, 6], "Unexpected result after flipX");
    315 }
    316 
    317 // DOMMatrix flipY();
    318 function testFlipY()
    319 {
    320  var m = createMatrix(1, 2, 3, 4, 5, 6);
    321  cmpMatrix(m.flipY(), [1, 2, -3, -4, 5, 6], "Unexpected result after flipY");
    322 }
    323 
    324 // DOMMatrix skewX(in float angle);
    325 function testSkewX()
    326 {
    327  var m = createMatrix(2, 0, 0, 1, 120, 100);
    328  roughCmpMatrix(m.skewX(30), [2, 0, 2*Math.tan(Math.PI/6), 1, 120, 100],
    329                 "Unexpected result after skewX");
    330 }
    331 
    332 // DOMMatrix skewY(in float angle);
    333 function testSkewY()
    334 {
    335  var m = createMatrix(2, 0, 0, 1, 120, 100);
    336  roughCmpMatrix(m.skewY(30), [2, Math.tan(Math.PI/6), 0, 1, 120, 100],
    337                 "Unexpected result after skewY");
    338 }
    339 
    340 // DOMMatrix multiply(in DOMMatrix secondMatrix);
    341 function testMultiplyInPlace()
    342 {
    343  var m1 = createMatrix(1, 0, 0, 1, 50, 90);
    344  var m2 = createMatrix(Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0);
    345  var m3 = createMatrix(1, 0, 0, 1, 130, 160);
    346  m1.multiplySelf(m2).multiplySelf(m3);
    347  roughCmpMatrix(m1, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 255.060974, 111.213203],
    348    "Unexpected result after multiplying matrices");
    349 
    350  // Check orig matrices are unchanged
    351  roughCmpMatrix(m2, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0],
    352                 "Matrix changed after multiplication");
    353  cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication");
    354 }
    355 
    356 // DOMMatrix inverse() raises(SVGException);
    357 function testInverseInPlace()
    358 {
    359  // Test inversion
    360  var m = createMatrix(2, 0, 0, 4, 110, -50);
    361  m.invertSelf();
    362  roughCmpMatrix(m, [0.5, 0, 0, 0.25, -55, 12.5],
    363    "Unexpected result after inverting matrix");
    364 
    365  // Test non-invertable
    366  m = createMatrix(0, 0, 1, 0, 0, 0);
    367  m.invertSelf();
    368  ok(isNaN(m.a), "Failed to invalidate inverted singular matrix, got " + m.a);
    369  ok(!m.is2D, "Failed to mark invalidated inverted singular matrix as 3D.");
    370 }
    371 
    372 // DOMMatrix translate(in float x, in float y);
    373 function testTranslateInPlace()
    374 {
    375  var m = createMatrix(2, 0, 0, 1, 120, 100);
    376  m.translateSelf(100, -50)
    377  roughCmpMatrix(m, [2, 0, 0, 1, 320, 50],
    378    "Unexpected result after translate");
    379 }
    380 
    381 // DOMMatrix scale(in float scaleFactor);
    382 function testScaleInPlace()
    383 {
    384  var m = createMatrix(2, 0, 0, 1, 120, 100);
    385  m.scaleSelf(0.5);
    386  roughCmpMatrix(m, [1, 0, 0, 0.5, 120, 100],
    387    "Unexpected result after scale");
    388 }
    389 
    390 // DOMMatrix rotate(in float angle);
    391 function testRotateInPlace()
    392 {
    393  var m = createMatrix(2, 0, 0, 1, 120, 100);
    394  m.rotateSelf(45);
    395  roughCmpMatrix(m,
    396                 [2*Math.cos(Math.PI/4), Math.sin(Math.PI/4),
    397                  2*-Math.sin(Math.PI/4), Math.cos(Math.PI/4),
    398                  120, 100],
    399                 "Unexpected result after rotate");
    400 }
    401 
    402 // DOMMatrix rotateFromVector(in float x, in float y) raises(SVGException);
    403 function testRotateFromVectorInPlace()
    404 {
    405  var m = createMatrix(2, 0, 0, 1, 120, 100);
    406  // Make a 150 degree angle
    407  m.rotateFromVectorSelf(-2, 1.1547);
    408  roughCmpMatrix(m,
    409                 [2*Math.cos(5*Math.PI/6), Math.sin(5*Math.PI/6),
    410                  2*-Math.sin(5*Math.PI/6), Math.cos(5*Math.PI/6),
    411                  120, 100],
    412                 "Unexpected result after rotateFromVector");
    413 
    414  // Test bad input (1)
    415  try {
    416    m.rotateFromVectorSelf(1, 0);
    417    ok(true, "did not throw exception with zero coord for rotateFromVector");
    418  } catch (e) {
    419    ok(false,
    420      "Got unexpected exception " + e + ", expected NotSupportedError");
    421  }
    422 
    423  // Test bad input (2)
    424  try {
    425    m.rotateFromVectorSelf(0, 1);
    426    ok(true, "did not throw exception with zero coord for rotateFromVector");
    427  } catch (e) { }
    428 }
    429 
    430 // DOMMatrix skewX(in float angle);
    431 function testSkewXInPlace()
    432 {
    433  var m = createMatrix(2, 0, 0, 1, 120, 100);
    434  m.skewXSelf(30);
    435  roughCmpMatrix(m, [2, 0, 2*Math.tan(Math.PI/6), 1, 120, 100],
    436                 "Unexpected result after skewX");
    437 }
    438 
    439 // DOMMatrix skewY(in float angle);
    440 function testSkewYInPlace()
    441 {
    442  var m = createMatrix(2, 0, 0, 1, 120, 100);
    443  m.skewYSelf(30);
    444  roughCmpMatrix(m, [2, Math.tan(Math.PI/6), 0, 1, 120, 100],
    445                 "Unexpected result after skewY");
    446 }
    447 
    448 function testCreateMatrix3D()
    449 {
    450  var m = new DOMMatrix([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
    451 
    452  // Should be initialised to identity
    453  cmpMatrix(m, [1, 0, 0, 1, 0, 0],
    454            "DOMMatrix should produce identity matrix");
    455  is(m.is2D, false, "should produce 3d matrix");
    456 
    457  m = new DOMMatrix([1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]);
    458 
    459  // Should be initialised to identity
    460  cmpMatrix(m, [1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
    461            "DOMMatrix should produce identity matrix");
    462  is(m.is2D, false, "should produce 3d matrix");
    463 }
    464 
    465 // DOMMatrix multiply(in DOMMatrix secondMatrix);
    466 function testMultiply3D()
    467 {
    468  var m1 = createMatrix(1, 0, 0, 1, 50, 90);
    469  var m2 = create3DMatrix(Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0);
    470  var m3 = create3DMatrix(1, 0, 0, 1, 130, 160);
    471  var result = m1.multiply(m2).multiply(m3);
    472  ok(m1.is2D == true, "should produce 3d matrix");
    473  roughCmpMatrix(result, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 255.060974, 111.213203],
    474    "Unexpected result after multiplying matrices");
    475 
    476  // Check orig matrices are unchanged
    477  cmpMatrix(m1, [1, 0, 0, 1, 50, 90], "Matrix changed after multiplication");
    478  roughCmpMatrix(m2, [Math.SQRT1_2, -Math.SQRT1_2, Math.SQRT1_2, Math.SQRT1_2, 0, 0],
    479                 "Matrix changed after multiplication");
    480  cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication");
    481 }
    482 
    483 // DOMMatrix inverse() raises(SVGException);
    484 function testInverse3D()
    485 {
    486  // Test inversion
    487  var m = create3DMatrix(2, 0, 0, 4, 110, -50);
    488  roughCmpMatrix(m.inverse(), [0.5, 0, 0, 0.25, -55, 12.5],
    489    "Unexpected result after inverting matrix");
    490 
    491  // Test non-invertable
    492  m = createMatrix(0, 0, 1, 0, 0, 0);
    493  m = m.inverse();
    494  ok(isNaN(m.a), "Failed to invalidate inverted singular matrix, got " + m.a);
    495  ok(!m.is2D, "Failed to mark invalidated inverted singular matrix as 3D.");
    496 }
    497 
    498 // DOMMatrix translate(in float x, in float y);
    499 function testTranslate3D()
    500 {
    501  var m = create3DMatrix(2, 0, 0, 1, 120, 100);
    502  roughCmpMatrix(m.translate(100, -50), [2, 0, 0, 1, 320, 50],
    503    "Unexpected result after translate");
    504 }
    505 
    506 // DOMMatrix scale(in float scaleFactor);
    507 function testScale3D()
    508 {
    509  var m = create3DMatrix(2, 0, 0, 1, 120, 100);
    510  roughCmpMatrix(m.scale(0.5), [1, 0, 0, 0.5, 120, 100],
    511    "Unexpected result after scale");
    512 }
    513 
    514 function Matrix3D() {
    515  this.m = new Float32Array([
    516      1, 0, 0, 0,
    517      0, 1, 0, 0,
    518      0, 0, 1, 0,
    519      0, 0, 0, 1
    520    ]);
    521 }
    522 
    523 Matrix3D.prototype = {
    524  translate(x, y, z, result) {
    525    result = result || new Matrix3D();
    526    var m = result.m;
    527 
    528    m[0] = 1;
    529    m[1] = 0;
    530    m[2] = 0;
    531    m[3] = x;
    532 
    533    m[4] = 0;
    534    m[5] = 1;
    535    m[6] = 0;
    536    m[7] = y;
    537 
    538    m[8] = 0;
    539    m[9] = 0;
    540    m[10] = 1;
    541    m[11] = z;
    542 
    543    m[12] = 0;
    544    m[13] = 0;
    545    m[14] = 0;
    546    m[15] = 1;
    547 
    548    return result;
    549  },
    550  inverse(matrix, result) {
    551    result = result || new Matrix3D();
    552    var m = matrix.m, r = result.m;
    553 
    554    r[0] = m[5]*m[10]*m[15] - m[5]*m[14]*m[11] - m[6]*m[9]*m[15] + m[6]*m[13]*m[11] + m[7]*m[9]*m[14] - m[7]*m[13]*m[10];
    555    r[1] = -m[1]*m[10]*m[15] + m[1]*m[14]*m[11] + m[2]*m[9]*m[15] - m[2]*m[13]*m[11] - m[3]*m[9]*m[14] + m[3]*m[13]*m[10];
    556    r[2] = m[1]*m[6]*m[15] - m[1]*m[14]*m[7] - m[2]*m[5]*m[15] + m[2]*m[13]*m[7] + m[3]*m[5]*m[14] - m[3]*m[13]*m[6];
    557    r[3] = -m[1]*m[6]*m[11] + m[1]*m[10]*m[7] + m[2]*m[5]*m[11] - m[2]*m[9]*m[7] - m[3]*m[5]*m[10] + m[3]*m[9]*m[6];
    558 
    559    r[4] = -m[4]*m[10]*m[15] + m[4]*m[14]*m[11] + m[6]*m[8]*m[15] - m[6]*m[12]*m[11] - m[7]*m[8]*m[14] + m[7]*m[12]*m[10];
    560    r[5] = m[0]*m[10]*m[15] - m[0]*m[14]*m[11] - m[2]*m[8]*m[15] + m[2]*m[12]*m[11] + m[3]*m[8]*m[14] - m[3]*m[12]*m[10];
    561    r[6] = -m[0]*m[6]*m[15] + m[0]*m[14]*m[7] + m[2]*m[4]*m[15] - m[2]*m[12]*m[7] - m[3]*m[4]*m[14] + m[3]*m[12]*m[6];
    562    r[7] = m[0]*m[6]*m[11] - m[0]*m[10]*m[7] - m[2]*m[4]*m[11] + m[2]*m[8]*m[7] + m[3]*m[4]*m[10] - m[3]*m[8]*m[6];
    563 
    564    r[8] = m[4]*m[9]*m[15] - m[4]*m[13]*m[11] - m[5]*m[8]*m[15] + m[5]*m[12]*m[11] + m[7]*m[8]*m[13] - m[7]*m[12]*m[9];
    565    r[9] = -m[0]*m[9]*m[15] + m[0]*m[13]*m[11] + m[1]*m[8]*m[15] - m[1]*m[12]*m[11] - m[3]*m[8]*m[13] + m[3]*m[12]*m[9];
    566    r[10] = m[0]*m[5]*m[15] - m[0]*m[13]*m[7] - m[1]*m[4]*m[15] + m[1]*m[12]*m[7] + m[3]*m[4]*m[13] - m[3]*m[12]*m[5];
    567    r[11] = -m[0]*m[5]*m[11] + m[0]*m[9]*m[7] + m[1]*m[4]*m[11] - m[1]*m[8]*m[7] - m[3]*m[4]*m[9] + m[3]*m[8]*m[5];
    568 
    569    r[12] = -m[4]*m[9]*m[14] + m[4]*m[13]*m[10] + m[5]*m[8]*m[14] - m[5]*m[12]*m[10] - m[6]*m[8]*m[13] + m[6]*m[12]*m[9];
    570    r[13] = m[0]*m[9]*m[14] - m[0]*m[13]*m[10] - m[1]*m[8]*m[14] + m[1]*m[12]*m[10] + m[2]*m[8]*m[13] - m[2]*m[12]*m[9];
    571    r[14] = -m[0]*m[5]*m[14] + m[0]*m[13]*m[6] + m[1]*m[4]*m[14] - m[1]*m[12]*m[6] - m[2]*m[4]*m[13] + m[2]*m[12]*m[5];
    572    r[15] = m[0]*m[5]*m[10] - m[0]*m[9]*m[6] - m[1]*m[4]*m[10] + m[1]*m[8]*m[6] + m[2]*m[4]*m[9] - m[2]*m[8]*m[5];
    573 
    574    var det = m[0]*r[0] + m[1]*r[4] + m[2]*r[8] + m[3]*r[12];
    575    for (var i = 0; i < 16; i++) r[i] /= det;
    576    return result;
    577  },
    578  multiply(left, result) {
    579    result = result || new Matrix3D();
    580    var a = this.m, b = left.m, r = result.m;
    581 
    582    r[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];
    583    r[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];
    584    r[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];
    585    r[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];
    586 
    587    r[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];
    588    r[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];
    589    r[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];
    590    r[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];
    591 
    592    r[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];
    593    r[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];
    594    r[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];
    595    r[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];
    596 
    597    r[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];
    598    r[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];
    599    r[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];
    600    r[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];
    601 
    602    return result;
    603  },
    604  scale(x, y, z, result) {
    605    result = result || new Matrix3D();
    606    var m = result.m;
    607 
    608    m[0] = x;
    609    m[1] = 0;
    610    m[2] = 0;
    611    m[3] = 0;
    612 
    613    m[4] = 0;
    614    m[5] = y;
    615    m[6] = 0;
    616    m[7] = 0;
    617 
    618    m[8] = 0;
    619    m[9] = 0;
    620    m[10] = z;
    621    m[11] = 0;
    622 
    623    m[12] = 0;
    624    m[13] = 0;
    625    m[14] = 0;
    626    m[15] = 1;
    627 
    628    return result;
    629  },
    630  rotate(a, x, y, z, result) {
    631    result = result || new Matrix3D();
    632    var m = result.m;
    633 
    634    var d = Math.sqrt(x*x + y*y + z*z);
    635    a *= Math.PI / 180; x /= d; y /= d; z /= d;
    636    var c = Math.cos(a), s = Math.sin(a), t = 1 - c;
    637 
    638    m[0] = x * x * t + c;
    639    m[1] = x * y * t - z * s;
    640    m[2] = x * z * t + y * s;
    641    m[3] = 0;
    642 
    643    m[4] = y * x * t + z * s;
    644    m[5] = y * y * t + c;
    645    m[6] = y * z * t - x * s;
    646    m[7] = 0;
    647 
    648    m[8] = z * x * t - y * s;
    649    m[9] = z * y * t + x * s;
    650    m[10] = z * z * t + c;
    651    m[11] = 0;
    652 
    653    m[12] = 0;
    654    m[13] = 0;
    655    m[14] = 0;
    656    m[15] = 1;
    657 
    658    return result;
    659  },
    660  swap(result) {
    661    result = result || new Matrix3D();
    662    for (var x = 0; x < 16; x++)
    663      result.m[x] = this.m[Math.floor(x/4) + (x%4)*4];
    664 
    665    return result;
    666  }
    667 };
    668 
    669 
    670 function test3D()
    671 {
    672  var m = new DOMMatrix()
    673  var m2 = new Matrix3D();
    674 
    675  m.translateSelf(2,3,4).scaleSelf(1.2, 2.3, 3.4, 0, 0, 0);
    676  m2 = m2.multiply(m2.translate(2,3,4)).multiply(m2.scale(1.2, 2.3, 3.4)).swap();
    677 
    678  ok(CompareMatrix(m2, m), "translate + scale in 3d didn't match, expected: " + formatMatrix(m2.m) + ", got: " + formatMatrix(m));
    679 
    680  m.invertSelf();
    681  m2 = new Matrix3D();
    682  m2 = m2.multiply(m2.translate(2,3,4)).multiply(m2.scale(1.2, 2.3, 3.4));
    683  m2 = m2.inverse(m2).swap();
    684  ok(CompareMatrix(m2, m), "translate + scale in inverted 3d didn't match, expected: " + formatMatrix(m2.m) + ", got: " + formatMatrix(m));
    685 }
    686 
    687 function testParsing()
    688 {
    689  var m = new DOMMatrix("translate(10px, 20px) scale(.5, 2) rotate(45deg)");
    690  var m2 = new DOMMatrix();
    691  m2.translateSelf(10, 20).scaleSelf(.5,2).rotateSelf(45);
    692  ok(CompareDOMMatrix(m2, m), "string parsing didn't match");
    693 
    694  m = new DOMMatrix();
    695  m.setMatrixValue("translate(10px, 20px) scale(.5, 2) rotate(45deg)");
    696  ok(CompareDOMMatrix(m2, m), "string parsing didn't match");
    697 }
    698 
    699 
    700 function testStringify() {
    701    var m = new DOMMatrix();
    702    var s = "" + m;
    703    ok(s == "matrix" + formatMatrix(m), "stringifier 1 produced wrong result: " + s);
    704    m.a = 100;
    705    s = "" + m;
    706    ok(s == "matrix" + formatMatrix(m), "stringifier 2 produced wrong result: " + s);
    707    m.m43 = 200;
    708    s = "" + m;
    709    ok(s == "matrix3d" + formatMatrix(m), "stringifier 3 produced wrong result:" + s);
    710 }
    711 
    712 window.addEventListener("load", main);
    713 
    714 </script>
    715 </pre>
    716 </body>
    717 </html>