Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(201)

Side by Side Diff: tests/MatrixTest.cpp

Issue 19569007: Add basic SVD support to SkMatrix. Allows you to pull out the x- and y-scale factors, sandwiched by… (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Remove trailing white space; replace fabs() with SkScalarAbs() Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/core/SkMatrixUtils.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 1
2 /* 2 /*
3 * Copyright 2011 Google Inc. 3 * Copyright 2011 Google Inc.
4 * 4 *
5 * Use of this source code is governed by a BSD-style license that can be 5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file. 6 * found in the LICENSE file.
7 */ 7 */
8 #include "Test.h" 8 #include "Test.h"
9 #include "SkMath.h" 9 #include "SkMath.h"
10 #include "SkMatrix.h" 10 #include "SkMatrix.h"
11 #include "SkMatrixUtils.h"
11 #include "SkRandom.h" 12 #include "SkRandom.h"
12 13
13 static bool nearly_equal_scalar(SkScalar a, SkScalar b) { 14 static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
14 // Note that we get more compounded error for multiple operations when 15 // Note that we get more compounded error for multiple operations when
15 // SK_SCALAR_IS_FIXED. 16 // SK_SCALAR_IS_FIXED.
16 #ifdef SK_SCALAR_IS_FLOAT 17 #ifdef SK_SCALAR_IS_FLOAT
17 const SkScalar tolerance = SK_Scalar1 / 200000; 18 const SkScalar tolerance = SK_Scalar1 / 200000;
18 #else 19 #else
19 const SkScalar tolerance = SK_Scalar1 / 1024; 20 const SkScalar tolerance = SK_Scalar1 / 1024;
20 #endif 21 #endif
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1); 339 mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
339 REPORTER_ASSERT(reporter, !mat.isSimilarity()); 340 REPORTER_ASSERT(reporter, !mat.isSimilarity());
340 341
341 // scales zero, only skews 342 // scales zero, only skews
342 mat.setAll(0, SK_Scalar1, 0, 343 mat.setAll(0, SK_Scalar1, 0,
343 SK_Scalar1, 0, 0, 344 SK_Scalar1, 0, 0,
344 0, 0, SkMatrix::I()[8]); 345 0, 0, SkMatrix::I()[8]);
345 REPORTER_ASSERT(reporter, mat.isSimilarity()); 346 REPORTER_ASSERT(reporter, mat.isSimilarity());
346 } 347 }
347 348
349 // For test_matrix_decomposition, below.
350 static bool scalar_nearly_equal_relative(SkScalar a, SkScalar b,
351 SkScalar tolerance = SK_ScalarNearlyZer o) {
352 // from Bruce Dawson
353 SkScalar diff = SkScalarAbs(a - b);
354 if (diff < tolerance) {
355 return true;
356 }
357
358 a = SkScalarAbs(a);
359 b = SkScalarAbs(b);
360 SkScalar largest = (b > a) ? b : a;
361
362 if (diff <= largest*tolerance) {
363 return true;
364 }
365
366 return false;
367 }
368
369 static void test_matrix_decomposition(skiatest::Reporter* reporter) {
370 SkMatrix mat;
371 SkScalar rotation0, scaleX, scaleY, rotation1;
372
373 const float kRotation0 = 15.5f;
374 const float kRotation1 = -50.f;
375 const float kScale0 = 5000.f;
376 const float kScale1 = 0.001f;
377
378 // identity
379 mat.reset();
380 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
381 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0));
382 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, SK_Scalar1));
383 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, SK_Scalar1));
384 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
385 // make sure it doesn't crash if we pass in NULLs
386 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL, NULL));
387
388 // rotation only
389 mat.setRotate(kRotation0);
390 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
391 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians( kRotation0)));
392 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, SK_Scalar1));
393 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, SK_Scalar1));
394 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
395
396 // uniform scale only
397 mat.setScale(kScale0, kScale0);
398 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
399 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0));
400 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
401 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
402 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
403
404 // anisotropic scale only
405 mat.setScale(kScale1, kScale0);
406 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
407 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0));
408 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1));
409 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
410 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
411
412 // rotation then uniform scale
413 mat.setRotate(kRotation1);
414 mat.postScale(kScale0, kScale0);
415 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
416 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians( kRotation1)));
417 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
418 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
419 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
420
421 // uniform scale then rotation
422 mat.setScale(kScale0, kScale0);
423 mat.postRotate(kRotation1);
424 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
425 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians( kRotation1)));
426 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
427 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
428 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
429
430 // rotation then uniform scale+reflection
431 mat.setRotate(kRotation0);
432 mat.postScale(kScale1, -kScale1);
433 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
434 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians( kRotation0)));
435 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1));
436 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, -kScale1));
437 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
438
439 // uniform scale+reflection, then rotate
440 mat.setScale(kScale0, -kScale0);
441 mat.postRotate(kRotation1);
442 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
443 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians( -kRotation1)));
444 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
445 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, -kScale0));
446 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
447
448 // rotation then anisotropic scale
449 mat.setRotate(kRotation1);
450 mat.postScale(kScale1, kScale0);
451 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
452 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians( kRotation1)));
453 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1));
454 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
455 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
456
457 // anisotropic scale then rotation
458 mat.setScale(kScale1, kScale0);
459 mat.postRotate(kRotation0);
460 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
461 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0));
462 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1));
463 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
464 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation1, SkDegreesToRadians( kRotation0)));
465
466 // rotation, uniform scale, then different rotation
467 mat.setRotate(kRotation1);
468 mat.postScale(kScale0, kScale0);
469 mat.postRotate(kRotation0);
470 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
471 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0,
472 SkDegreesToRadians(kRotation0 + kRotation1)));
473 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0));
474 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0));
475 REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1));
476
477 // rotation, anisotropic scale, then different rotation
478 mat.setRotate(kRotation0);
479 mat.postScale(kScale1, kScale0);
480 mat.postRotate(kRotation1);
481 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
482 // Because of the shear/skew we won't get the same results, so we need to mu ltiply it out.
483 // Generating the matrices requires doing a radian-to-degree calculation, th en degree-to-radian
484 // calculation (in setRotate()), which adds error, so this just computes the matrix elements
485 // directly.
486 SkScalar c0;
487 SkScalar s0 = SkScalarSinCos(rotation0, &c0);
488 SkScalar c1;
489 SkScalar s1 = SkScalarSinCos(rotation1, &c1);
490 // We do a relative check here because large scale factors cause problems wi th an absolute check
491 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale X],
492 scaleX*c0*c1 - scaleY *s0*s1));
493 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX ],
494 -scaleX*s0*c1 - scale Y*c0*s1));
495 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY ],
496 scaleX*c0*s1 + scaleY *s0*c1));
497 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale Y],
498 -scaleX*s0*s1 + scale Y*c0*c1));
499
500 // try some random matrices
501 SkMWCRandom rand;
502 for (int m = 0; m < 1000; ++m) {
503 SkScalar rot0 = rand.nextRangeF(-SK_ScalarPI, SK_ScalarPI);
504 SkScalar sx = rand.nextRangeF(-3000.f, 3000.f);
505 SkScalar sy = rand.nextRangeF(-3000.f, 3000.f);
506 SkScalar rot1 = rand.nextRangeF(-SK_ScalarPI, SK_ScalarPI);
507 mat.setRotate(rot0);
508 mat.postScale(sx, sy);
509 mat.postRotate(rot1);
510
511 if (SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)) {
512 SkScalar c0;
513 SkScalar s0 = SkScalarSinCos(rotation0, &c0);
514 SkScalar c1;
515 SkScalar s1 = SkScalarSinCos(rotation1, &c1);
516 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix: :kMScaleX],
517 scaleX*c0*c1 - scaleY*s0*s1));
518 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix: :kMSkewX],
519 -scaleX*s0*c1 - scaleY*c0*s1));
520 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix: :kMSkewY],
521 scaleX*c0*s1 + scaleY*s0*c1));
522 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix: :kMScaleY],
523 -scaleX*s0*s1 + scaleY*c0*c1));
524 } else {
525 // if the matrix is degenerate, the basis vectors should be near-par allel or near-zero
526 SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] -
527 mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY];
528 REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot));
529 }
530 }
531
532 // translation shouldn't affect this
533 mat.postTranslate(-1000.f, 1000.f);
534 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
535 s0 = SkScalarSinCos(rotation0, &c0);
536 s1 = SkScalarSinCos(rotation1, &c1);
537 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale X],
538 scaleX*c0*c1 - scaleY *s0*s1));
539 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX ],
540 -scaleX*s0*c1 - scale Y*c0*s1));
541 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY ],
542 scaleX*c0*s1 + scaleY *s0*c1));
543 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale Y],
544 -scaleX*s0*s1 + scale Y*c0*c1));
545
546 // perspective shouldn't affect this
547 mat[SkMatrix::kMPersp0] = 12.0;
548 mat[SkMatrix::kMPersp1] = 4.0;
549 mat[SkMatrix::kMPersp2] = 1872.0;
550 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
551 s0 = SkScalarSinCos(rotation0, &c0);
552 s1 = SkScalarSinCos(rotation1, &c1);
553 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale X],
554 scaleX*c0*c1 - scaleY *s0*s1));
555 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX ],
556 -scaleX*s0*c1 - scale Y*c0*s1));
557 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY ],
558 scaleX*c0*s1 + scaleY *s0*c1));
559 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale Y],
560 -scaleX*s0*s1 + scale Y*c0*c1));
561
562 // rotation, anisotropic scale + reflection, then different rotation
563 mat.setRotate(kRotation0);
564 mat.postScale(-kScale1, kScale0);
565 mat.postRotate(kRotation1);
566 REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sca leY, &rotation1));
567 s0 = SkScalarSinCos(rotation0, &c0);
568 s1 = SkScalarSinCos(rotation1, &c1);
569 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale X],
570 scaleX*c0*c1 - scaleY *s0*s1));
571 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX ],
572 -scaleX*s0*c1 - scale Y*c0*s1));
573 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY ],
574 scaleX*c0*s1 + scaleY *s0*c1));
575 REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScale Y],
576 -scaleX*s0*s1 + scale Y*c0*c1));
577
578 // degenerate matrices
579 // mostly zero entries
580 mat.reset();
581 mat[SkMatrix::kMScaleX] = 0.f;
582 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sc aleY, &rotation1));
583 mat.reset();
584 mat[SkMatrix::kMScaleY] = 0.f;
585 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sc aleY, &rotation1));
586 mat.reset();
587 // linearly dependent entries
588 mat[SkMatrix::kMScaleX] = 1.f;
589 mat[SkMatrix::kMSkewX] = 2.f;
590 mat[SkMatrix::kMSkewY] = 4.f;
591 mat[SkMatrix::kMScaleY] = 8.f;
592 REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &sc aleY, &rotation1));
593 }
594
348 static void TestMatrix(skiatest::Reporter* reporter) { 595 static void TestMatrix(skiatest::Reporter* reporter) {
349 SkMatrix mat, inverse, iden1, iden2; 596 SkMatrix mat, inverse, iden1, iden2;
350 597
351 mat.reset(); 598 mat.reset();
352 mat.setTranslate(SK_Scalar1, SK_Scalar1); 599 mat.setTranslate(SK_Scalar1, SK_Scalar1);
353 REPORTER_ASSERT(reporter, mat.invert(&inverse)); 600 REPORTER_ASSERT(reporter, mat.invert(&inverse));
354 iden1.setConcat(mat, inverse); 601 iden1.setConcat(mat, inverse);
355 REPORTER_ASSERT(reporter, is_identity(iden1)); 602 REPORTER_ASSERT(reporter, is_identity(iden1));
356 603
357 mat.setScale(SkIntToScalar(2), SkIntToScalar(4)); 604 mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
458 // fixed pt doesn't have the property that NaN does not equal itself. 705 // fixed pt doesn't have the property that NaN does not equal itself.
459 #ifdef SK_SCALAR_IS_FIXED 706 #ifdef SK_SCALAR_IS_FIXED
460 REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2)); 707 REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
461 #else 708 #else
462 REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2)); 709 REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
463 #endif 710 #endif
464 711
465 test_matrix_max_stretch(reporter); 712 test_matrix_max_stretch(reporter);
466 test_matrix_is_similarity(reporter); 713 test_matrix_is_similarity(reporter);
467 test_matrix_recttorect(reporter); 714 test_matrix_recttorect(reporter);
715 test_matrix_decomposition(reporter);
468 } 716 }
469 717
470 #include "TestClassDef.h" 718 #include "TestClassDef.h"
471 DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix) 719 DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix)
OLDNEW
« no previous file with comments | « src/core/SkMatrixUtils.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698