diff options
author | shawnsingh@google.com <shawnsingh@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-28 23:49:26 +0000 |
---|---|---|
committer | shawnsingh@google.com <shawnsingh@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-28 23:49:26 +0000 |
commit | 2c7cd6d2b9b4180cbf8f5067cf9c21e3c8051b73 (patch) | |
tree | fc3598a693c1649888f42c14485cfaa8c8c448ba | |
parent | f657203ad9a1a67d54d507652b613e80f9f80a15 (diff) | |
download | chromium_src-2c7cd6d2b9b4180cbf8f5067cf9c21e3c8051b73.zip chromium_src-2c7cd6d2b9b4180cbf8f5067cf9c21e3c8051b73.tar.gz chromium_src-2c7cd6d2b9b4180cbf8f5067cf9c21e3c8051b73.tar.bz2 |
Move temporary MathUtil wrappers to permanent home in gfx::Transform
This patch moves some of the temporary wrappers to their permanent home. Unit
tests remain in math_util_unittests for now, but no test coverage is lost.
Added 3 static accessors to Vector3dF help readability when calling RotateAbout().
BUG=159972
Review URL: https://codereview.chromium.org/11418197
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@170099 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | cc/layer_sorter_unittest.cc | 6 | ||||
-rw-r--r-- | cc/layer_tree_host_common.cc | 18 | ||||
-rw-r--r-- | cc/layer_tree_host_common_unittest.cc | 8 | ||||
-rw-r--r-- | cc/math_util.cc | 63 | ||||
-rw-r--r-- | cc/math_util.h | 4 | ||||
-rw-r--r-- | cc/math_util_unittest.cc | 267 | ||||
-rw-r--r-- | cc/occlusion_tracker_unittest.cc | 4 | ||||
-rw-r--r-- | ui/gfx/transform.cc | 126 | ||||
-rw-r--r-- | ui/gfx/transform.h | 23 |
9 files changed, 378 insertions, 141 deletions
diff --git a/cc/layer_sorter_unittest.cc b/cc/layer_sorter_unittest.cc index 9b13fce..94aa7b5 100644 --- a/cc/layer_sorter_unittest.cc +++ b/cc/layer_sorter_unittest.cc @@ -64,7 +64,7 @@ TEST(LayerSorterTest, RightAngleOverlap) // Two layers forming a right angle with a perspective viewing transform. gfx::Transform leftFaceMatrix; leftFaceMatrix.Translate3d(-1, 0, -5); - MathUtil::rotateAxisAngle(&leftFaceMatrix, 0, 1, 0, -90); + leftFaceMatrix.RotateAboutYAxis(-90); leftFaceMatrix.Translate(-1, -1); LayerShape leftFace(2, 2, perspectiveMatrix * leftFaceMatrix); gfx::Transform frontFaceMatrix; @@ -94,7 +94,7 @@ TEST(LayerSorterTest, IntersectingLayerOverlap) gfx::Transform throughMatrix; throughMatrix.Translate3d(0, 0, -4); - MathUtil::rotateAxisAngle(&throughMatrix, 0, 1, 0, 45); + throughMatrix.RotateAboutYAxis(45); throughMatrix.Translate(-1, -1); LayerShape rotatedFace(2, 2, perspectiveMatrix * throughMatrix); overlapResult = LayerSorter::checkOverlap(&frontFace, &rotatedFace, zThreshold, weight); @@ -131,7 +131,7 @@ TEST(LayerSorterTest, LayersAtAngleOverlap) LayerShape layerB(8, 20, transformB); gfx::Transform transformC; - MathUtil::rotateAxisAngle(&transformC, 0, 1, 0, 40); + transformC.RotateAboutYAxis(40); transformC.Translate(-4, -10); LayerShape layerC(8, 20, transformC); diff --git a/cc/layer_tree_host_common.cc b/cc/layer_tree_host_common.cc index b6c4c4e..0d6909c 100644 --- a/cc/layer_tree_host_common.cc +++ b/cc/layer_tree_host_common.cc @@ -85,21 +85,21 @@ static bool isLayerBackFaceVisible(LayerType* layer) // rendering context by checking if the parent preserves 3d. if (layerIsInExisting3DRenderingContext(layer)) - return MathUtil::isBackFaceVisible(layer->drawTransform()); + return layer->drawTransform().IsBackFaceVisible(); // In this case, either the layer establishes a new 3d rendering context, or is not in // a 3d rendering context at all. - return MathUtil::isBackFaceVisible(layer->transform()); + return layer->transform().IsBackFaceVisible(); } template<typename LayerType> static bool isSurfaceBackFaceVisible(LayerType* layer, const gfx::Transform& drawTransform) { if (layerIsInExisting3DRenderingContext(layer)) - return MathUtil::isBackFaceVisible(drawTransform); + return drawTransform.IsBackFaceVisible(); if (isRootLayerOfNewRenderingContext(layer)) - return MathUtil::isBackFaceVisible(layer->transform()); + return layer->transform().IsBackFaceVisible(); // If the renderSurface is not part of a new or existing rendering context, then the // layers that contribute to this surface will decide back-face visibility for themselves. @@ -140,14 +140,6 @@ static gfx::Rect calculateVisibleContentRect(LayerType* layer) return LayerTreeHostCommon::calculateVisibleRect(targetSurfaceClipRect, gfx::Rect(gfx::Point(), layer->contentBounds()), layer->drawTransform()); } -static bool isScaleOrTranslation(const gfx::Transform& m) -{ - return !m.matrix().getDouble(1, 0) && !m.matrix().getDouble(2, 0) && !m.matrix().getDouble(3, 0) - && !m.matrix().getDouble(0, 1) && !m.matrix().getDouble(2, 1) && !m.matrix().getDouble(3, 1) - && !m.matrix().getDouble(0, 2) && !m.matrix().getDouble(1, 2) && !m.matrix().getDouble(2, 3) - && m.matrix().getDouble(3, 3); -} - static inline bool transformToParentIsKnown(LayerImpl*) { return true; @@ -562,7 +554,7 @@ static void calculateDrawTransformsInternal(LayerType* layer, const gfx::Transfo gfx::Vector2dF renderSurfaceSublayerScale = MathUtil::computeTransform2dScaleComponents(combinedTransform); - if (subtreeShouldRenderToSeparateSurface(layer, isScaleOrTranslation(combinedTransform))) { + if (subtreeShouldRenderToSeparateSurface(layer, combinedTransform.IsScaleOrTranslation())) { // Check back-face visibility before continuing with this surface and its subtree if (!layer->doubleSided() && transformToParentIsKnown(layer) && isSurfaceBackFaceVisible(layer, combinedTransform)) return; diff --git a/cc/layer_tree_host_common_unittest.cc b/cc/layer_tree_host_common_unittest.cc index 0623a36..6933335 100644 --- a/cc/layer_tree_host_common_unittest.cc +++ b/cc/layer_tree_host_common_unittest.cc @@ -2457,7 +2457,7 @@ TEST(LayerTreeHostCommonTest, verifyBackFaceCullingWithoutPreserves3d) gfx::Transform backfaceMatrix; backfaceMatrix.Translate(50, 50); - MathUtil::rotateAxisAngle(&backfaceMatrix, 0, 1, 0, 180); + backfaceMatrix.RotateAboutYAxis(180); backfaceMatrix.Translate(-50, -50); // Having a descendant and opacity will force these to have render surfaces. @@ -2551,7 +2551,7 @@ TEST(LayerTreeHostCommonTest, verifyBackFaceCullingWithPreserves3d) gfx::Transform backfaceMatrix; backfaceMatrix.Translate(50, 50); - MathUtil::rotateAxisAngle(&backfaceMatrix, 0, 1, 0, 180); + backfaceMatrix.RotateAboutYAxis(180); backfaceMatrix.Translate(-50, -50); // Opacity will not force creation of renderSurfaces in this case because of the @@ -2637,7 +2637,7 @@ TEST(LayerTreeHostCommonTest, verifyBackFaceCullingWithAnimatingTransforms) gfx::Transform backfaceMatrix; backfaceMatrix.Translate(50, 50); - MathUtil::rotateAxisAngle(&backfaceMatrix, 0, 1, 0, 180); + backfaceMatrix.RotateAboutYAxis(180); backfaceMatrix.Translate(-50, -50); // Make our render surface. @@ -2712,7 +2712,7 @@ TEST(LayerTreeHostCommonTest, verifyBackFaceCullingWithPreserves3dForFlatteningS gfx::Transform backfaceMatrix; backfaceMatrix.Translate(50, 50); - MathUtil::rotateAxisAngle(&backfaceMatrix, 0, 1, 0, 180); + backfaceMatrix.RotateAboutYAxis(180); backfaceMatrix.Translate(-50, -50); setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, gfx::PointF(0, 0), gfx::PointF(0, 0), gfx::Size(100, 100), true); // parent transform style is preserve3d. diff --git a/cc/math_util.cc b/cc/math_util.cc index 9312d10..2e0349c 100644 --- a/cc/math_util.cc +++ b/cc/math_util.cc @@ -107,7 +107,7 @@ gfx::Rect MathUtil::mapClippedRect(const gfx::Transform& transform, const gfx::R gfx::RectF MathUtil::mapClippedRect(const gfx::Transform& transform, const gfx::RectF& srcRect) { - if (MathUtil::isIdentityOrTranslation(transform)) + if (transform.IsIdentityOrTranslation()) return srcRect + gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3))); // Apply the transform, but retain the result in homogeneous coordinates. @@ -122,7 +122,7 @@ gfx::RectF MathUtil::mapClippedRect(const gfx::Transform& transform, const gfx:: gfx::RectF MathUtil::projectClippedRect(const gfx::Transform& transform, const gfx::RectF& srcRect) { - if (MathUtil::isIdentityOrTranslation(transform)) + if (transform.IsIdentityOrTranslation()) return srcRect + gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3))); // Perform the projection, but retain the result in homogeneous coordinates. @@ -241,7 +241,7 @@ gfx::RectF MathUtil::computeEnclosingClippedRect(const HomogeneousCoordinate& h1 gfx::QuadF MathUtil::mapQuad(const gfx::Transform& transform, const gfx::QuadF& q, bool& clipped) { - if (MathUtil::isIdentityOrTranslation(transform)) { + if (transform.IsIdentityOrTranslation()) { gfx::QuadF mappedQuad(q); mappedQuad += gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)), static_cast<float>(transform.matrix().getDouble(1, 3))); clipped = false; @@ -373,7 +373,7 @@ static inline float scaleOnAxis(double a, double b, double c) gfx::Vector2dF MathUtil::computeTransform2dScaleComponents(const gfx::Transform& transform) { - if (hasPerspective(transform)) + if (transform.HasPerspective()) return gfx::Vector2dF(1, 1); float xScale = scaleOnAxis(transform.matrix().getDouble(0, 0), transform.matrix().getDouble(1, 0), transform.matrix().getDouble(2, 0)); float yScale = scaleOnAxis(transform.matrix().getDouble(0, 1), transform.matrix().getDouble(1, 1), transform.matrix().getDouble(2, 1)); @@ -394,69 +394,22 @@ gfx::Vector2dF MathUtil::projectVector(gfx::Vector2dF source, gfx::Vector2dF des return gfx::Vector2dF(projectedLength * destination.x(), projectedLength * destination.y()); } -bool MathUtil::isBackFaceVisible(const gfx::Transform& transform) -{ - // Compute whether a layer with a forward-facing normal of (0, 0, 1) would - // have its back face visible after applying the transform. - // - // This is done by transforming the normal and seeing if the resulting z - // value is positive or negative. However, note that transforming a normal - // actually requires using the inverse-transpose of the original transform. - - // TODO (shawnsingh) make this perform more efficiently - we do not - // actually need to instantiate/invert/transpose any matrices, exploiting the - // fact that we only need to transform (0, 0, 1, 0). - gfx::Transform inverseTransform = MathUtil::inverse(transform); - const SkMatrix44& mInv = inverseTransform.matrix(); - - return mInv.getDouble(2, 2) < 0; -} - -bool MathUtil::isIdentityOrTranslation(const gfx::Transform& transform) -{ - const SkMatrix44& matrix = transform.matrix(); - - bool hasNoPerspective = !matrix.getDouble(3, 0) && !matrix.getDouble(3, 1) && !matrix.getDouble(3, 2) && (matrix.getDouble(3, 3) == 1); - bool hasNoRotationOrSkew = !matrix.getDouble(0, 1) && !matrix.getDouble(0, 2) && !matrix.getDouble(1, 0) && - !matrix.getDouble(1, 2) && !matrix.getDouble(2, 0) && !matrix.getDouble(2, 1); - bool hasNoScale = matrix.getDouble(0, 0) == 1 && matrix.getDouble(1, 1) == 1 && matrix.getDouble(2, 2) == 1; - - return hasNoPerspective && hasNoRotationOrSkew && hasNoScale; -} - -bool MathUtil::hasPerspective(const gfx::Transform& transform) -{ - // Mathematically it is a bit too strict to expect the 4th element to be - // equal to 1. However, the only non-perspective case where this element - // becomes non-1 is when it was explicitly initialized. In that case it - // still causes us to have a nontrivial divide-by-w, so we count it as - // being perspective here. - const SkMatrix44& matrix = transform.matrix(); - return matrix.getDouble(3, 0) || matrix.getDouble(3, 1) || matrix.getDouble(3, 2) || (matrix.getDouble(3, 3) != 1); -} - void MathUtil::rotateEulerAngles(gfx::Transform* transform, double eulerX, double eulerY, double eulerZ) { // TODO (shawnsingh): make this implementation faster and more accurate by - // hard-coding each matrix instead of calling rotateAxisAngle(). + // hard-coding each matrix instead of calling RotateAbout(). gfx::Transform rotationAboutX; gfx::Transform rotationAboutY; gfx::Transform rotationAboutZ; - MathUtil::rotateAxisAngle(&rotationAboutX, 1, 0, 0, eulerX); - MathUtil::rotateAxisAngle(&rotationAboutY, 0, 1, 0, eulerY); - MathUtil::rotateAxisAngle(&rotationAboutZ, 0, 0, 1, eulerZ); + rotationAboutX.RotateAboutXAxis(eulerX); + rotationAboutY.RotateAboutYAxis(eulerY); + rotationAboutZ.RotateAboutZAxis(eulerZ); gfx::Transform composite = rotationAboutZ * rotationAboutY * rotationAboutX; transform->PreconcatTransform(composite); } -void MathUtil::rotateAxisAngle(gfx::Transform* transform, double i, double j, double k, double degrees) -{ - gfx::Vector3dF axis(i, j, k); - transform->RotateAbout(axis, degrees); -} - gfx::Transform MathUtil::inverse(const gfx::Transform& transform) { gfx::Transform result; diff --git a/cc/math_util.h b/cc/math_util.h index 99ee46c..e3ed2d3 100644 --- a/cc/math_util.h +++ b/cc/math_util.h @@ -120,11 +120,7 @@ public: // // TODO(shawnsingh, vollick) we should phase out as much as possible of // these temporary functions, putting functionality into gfx::Transform. - static bool isBackFaceVisible(const gfx::Transform&); - static bool isIdentityOrTranslation(const gfx::Transform&); - static bool hasPerspective(const gfx::Transform&); static void rotateEulerAngles(gfx::Transform*, double eulerX, double eulerY, double eulerZ); - static void rotateAxisAngle(gfx::Transform*, double i, double j, double k, double degrees); static gfx::Transform inverse(const gfx::Transform&); static gfx::Transform to2dTransform(const gfx::Transform&); // Note carefully: the args here are labeled as per Webcore indexing conventions. diff --git a/cc/math_util_unittest.cc b/cc/math_util_unittest.cc index c9daf9c..0aa0523 100644 --- a/cc/math_util_unittest.cc +++ b/cc/math_util_unittest.cc @@ -21,27 +21,27 @@ TEST(MathUtilTest, verifyBackfaceVisibilityBasicCases) gfx::Transform transform; transform.MakeIdentity(); - EXPECT_FALSE(MathUtil::isBackFaceVisible(transform)); + EXPECT_FALSE(transform.IsBackFaceVisible()); transform.MakeIdentity(); MathUtil::rotateEulerAngles(&transform, 0, 80, 0); - EXPECT_FALSE(MathUtil::isBackFaceVisible(transform)); + EXPECT_FALSE(transform.IsBackFaceVisible()); transform.MakeIdentity(); MathUtil::rotateEulerAngles(&transform, 0, 100, 0); - EXPECT_TRUE(MathUtil::isBackFaceVisible(transform)); + EXPECT_TRUE(transform.IsBackFaceVisible()); // Edge case, 90 degree rotation should return false. transform.MakeIdentity(); MathUtil::rotateEulerAngles(&transform, 0, 90, 0); - EXPECT_FALSE(MathUtil::isBackFaceVisible(transform)); + EXPECT_FALSE(transform.IsBackFaceVisible()); } TEST(MathUtilTest, verifyBackfaceVisibilityForPerspective) { gfx::Transform layerSpaceToProjectionPlane; - // This tests if isBackFaceVisible works properly under perspective transforms. + // This tests if IsBackFaceVisible works properly under perspective transforms. // Specifically, layers that may have their back face visible in orthographic // projection, may not actually have back face visible under perspective projection. @@ -52,7 +52,7 @@ TEST(MathUtilTest, verifyBackfaceVisibilityForPerspective) layerSpaceToProjectionPlane.ApplyPerspectiveDepth(1); layerSpaceToProjectionPlane.Translate3d(0, 0, 0); MathUtil::rotateEulerAngles(&layerSpaceToProjectionPlane, 0, 100, 0); - EXPECT_TRUE(MathUtil::isBackFaceVisible(layerSpaceToProjectionPlane)); + EXPECT_TRUE(layerSpaceToProjectionPlane.IsBackFaceVisible()); // Case 2: Layer is rotated by slightly more than 90 degrees, but shifted off to the // side of the camera. Because of the wide field-of-view, the layer's front @@ -72,12 +72,12 @@ TEST(MathUtilTest, verifyBackfaceVisibilityForPerspective) layerSpaceToProjectionPlane.ApplyPerspectiveDepth(1); layerSpaceToProjectionPlane.Translate3d(-10, 0, 0); MathUtil::rotateEulerAngles(&layerSpaceToProjectionPlane, 0, 100, 0); - EXPECT_FALSE(MathUtil::isBackFaceVisible(layerSpaceToProjectionPlane)); + EXPECT_FALSE(layerSpaceToProjectionPlane.IsBackFaceVisible()); // Case 3: Additionally rotating the layer by 180 degrees should of course show the // opposite result of case 2. MathUtil::rotateEulerAngles(&layerSpaceToProjectionPlane, 0, 180, 0); - EXPECT_TRUE(MathUtil::isBackFaceVisible(layerSpaceToProjectionPlane)); + EXPECT_TRUE(layerSpaceToProjectionPlane.IsBackFaceVisible()); } TEST(MathUtilTest, verifyProjectionOfPerpendicularPlane) @@ -727,13 +727,104 @@ TEST(MathUtilGfxTransformTest, verifyRotateEulerAnglesOrderOfCompositeRotations) EXPECT_ROW4_EQ(0, 0, 0, 1, A); } -TEST(MathUtilGfxTransformTest, verifyRotateAxisAngleForAlignedAxes) +TEST(MathUtilGfxTransformTest, verifyRotateAboutXAxis) +{ + gfx::Transform A; + double sin45 = 0.5 * sqrt(2.0); + double cos45 = sin45; + + A.MakeIdentity(); + A.RotateAboutXAxis(90); + EXPECT_ROW1_EQ(1, 0, 0, 0, A); + EXPECT_ROW2_NEAR(0, 0, -1, 0, A, ERROR_THRESHOLD); + EXPECT_ROW3_NEAR(0, 1, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW4_EQ(0, 0, 0, 1, A); + + A.MakeIdentity(); + A.RotateAboutXAxis(45); + EXPECT_ROW1_EQ(1, 0, 0, 0, A); + EXPECT_ROW2_NEAR(0, cos45, -sin45, 0, A, ERROR_THRESHOLD); + EXPECT_ROW3_NEAR(0, sin45, cos45, 0, A, ERROR_THRESHOLD); + EXPECT_ROW4_EQ(0, 0, 0, 1, A); + + // Verify that rotateAboutXAxis(angle) post-multiplies the existing matrix. + A.MakeIdentity(); + A.Scale3d(6, 7, 8); + A.RotateAboutXAxis(90); + EXPECT_ROW1_NEAR(6, 0, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW2_NEAR(0, 0, -7, 0, A, ERROR_THRESHOLD); + EXPECT_ROW3_NEAR(0, 8, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW4_EQ(0, 0, 0, 1, A); +} + +TEST(MathUtilGfxTransformTest, verifyRotateAboutYAxis) +{ + gfx::Transform A; + double sin45 = 0.5 * sqrt(2.0); + double cos45 = sin45; + + // Note carefully, the expected pattern is inverted compared to rotating about x axis or z axis. + A.MakeIdentity(); + A.RotateAboutYAxis(90); + EXPECT_ROW1_NEAR(0, 0, 1, 0, A, ERROR_THRESHOLD); + EXPECT_ROW2_EQ(0, 1, 0, 0, A); + EXPECT_ROW3_NEAR(-1, 0, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW4_EQ(0, 0, 0, 1, A); + + A.MakeIdentity(); + A.RotateAboutYAxis(45); + EXPECT_ROW1_NEAR(cos45, 0, sin45, 0, A, ERROR_THRESHOLD); + EXPECT_ROW2_EQ(0, 1, 0, 0, A); + EXPECT_ROW3_NEAR(-sin45, 0, cos45, 0, A, ERROR_THRESHOLD); + EXPECT_ROW4_EQ(0, 0, 0, 1, A); + + // Verify that rotateAboutYAxis(angle) post-multiplies the existing matrix. + A.MakeIdentity(); + A.Scale3d(6, 7, 8); + A.RotateAboutYAxis(90); + EXPECT_ROW1_NEAR(0, 0, 6, 0, A, ERROR_THRESHOLD); + EXPECT_ROW2_NEAR(0, 7, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW3_NEAR(-8, 0, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW4_EQ(0, 0, 0, 1, A); +} + +TEST(MathUtilGfxTransformTest, verifyRotateAboutZAxis) +{ + gfx::Transform A; + double sin45 = 0.5 * sqrt(2.0); + double cos45 = sin45; + + A.MakeIdentity(); + A.RotateAboutZAxis(90); + EXPECT_ROW1_NEAR(0, -1, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW2_NEAR(1, 0, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW3_EQ(0, 0, 1, 0, A); + EXPECT_ROW4_EQ(0, 0, 0, 1, A); + + A.MakeIdentity(); + A.RotateAboutZAxis(45); + EXPECT_ROW1_NEAR(cos45, -sin45, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW2_NEAR(sin45, cos45, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW3_EQ(0, 0, 1, 0, A); + EXPECT_ROW4_EQ(0, 0, 0, 1, A); + + // Verify that rotateAboutZAxis(angle) post-multiplies the existing matrix. + A.MakeIdentity(); + A.Scale3d(6, 7, 8); + A.RotateAboutZAxis(90); + EXPECT_ROW1_NEAR(0, -6, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW2_NEAR(7, 0, 0, 0, A, ERROR_THRESHOLD); + EXPECT_ROW3_EQ(0, 0, 8, 0, A); + EXPECT_ROW4_EQ(0, 0, 0, 1, A); +} + +TEST(MathUtilGfxTransformTest, verifyRotateAboutForAlignedAxes) { gfx::Transform A; // Check rotation about z-axis A.MakeIdentity(); - MathUtil::rotateAxisAngle(&A, 0, 0, 1, 90); + A.RotateAbout(gfx::Vector3dF(0, 0, 1), 90); EXPECT_ROW1_NEAR(0, -1, 0, 0, A, ERROR_THRESHOLD); EXPECT_ROW2_NEAR(1, 0, 0, 0, A, ERROR_THRESHOLD); EXPECT_ROW3_EQ(0, 0, 1, 0, A); @@ -741,7 +832,7 @@ TEST(MathUtilGfxTransformTest, verifyRotateAxisAngleForAlignedAxes) // Check rotation about x-axis A.MakeIdentity(); - MathUtil::rotateAxisAngle(&A, 1, 0, 0, 90); + A.RotateAbout(gfx::Vector3dF(1, 0, 0), 90); EXPECT_ROW1_EQ(1, 0, 0, 0, A); EXPECT_ROW2_NEAR(0, 0, -1, 0, A, ERROR_THRESHOLD); EXPECT_ROW3_NEAR(0, 1, 0, 0, A, ERROR_THRESHOLD); @@ -750,7 +841,7 @@ TEST(MathUtilGfxTransformTest, verifyRotateAxisAngleForAlignedAxes) // Check rotation about y-axis. // Note carefully, the expected pattern is inverted compared to rotating about x axis or z axis. A.MakeIdentity(); - MathUtil::rotateAxisAngle(&A, 0, 1, 0, 90); + A.RotateAbout(gfx::Vector3dF(0, 1, 0), 90); EXPECT_ROW1_NEAR(0, 0, 1, 0, A, ERROR_THRESHOLD); EXPECT_ROW2_EQ(0, 1, 0, 0, A); EXPECT_ROW3_NEAR(-1, 0, 0, 0, A, ERROR_THRESHOLD); @@ -759,18 +850,18 @@ TEST(MathUtilGfxTransformTest, verifyRotateAxisAngleForAlignedAxes) // Verify that rotate3d(axis, angle) post-multiplies the existing matrix. A.MakeIdentity(); A.Scale3d(6, 7, 8); - MathUtil::rotateAxisAngle(&A, 0, 0, 1, 90); + A.RotateAboutZAxis(90); EXPECT_ROW1_NEAR(0, -6, 0, 0, A, ERROR_THRESHOLD); EXPECT_ROW2_NEAR(7, 0, 0, 0, A, ERROR_THRESHOLD); EXPECT_ROW3_EQ(0, 0, 8, 0, A); EXPECT_ROW4_EQ(0, 0, 0, 1, A); } -TEST(MathUtilGfxTransformTest, verifyRotateAxisAngleForArbitraryAxis) +TEST(MathUtilGfxTransformTest, verifyRotateAboutForArbitraryAxis) { // Check rotation about an arbitrary non-axis-aligned vector. gfx::Transform A; - MathUtil::rotateAxisAngle(&A, 1, 1, 1, 90); + A.RotateAbout(gfx::Vector3dF(1, 1, 1), 90); EXPECT_ROW1_NEAR(0.3333333333333334258519187, -0.2440169358562924717404030, 0.9106836025229592124219380, @@ -786,18 +877,18 @@ TEST(MathUtilGfxTransformTest, verifyRotateAxisAngleForArbitraryAxis) EXPECT_ROW4_EQ(0, 0, 0, 1, A); } -TEST(MathUtilGfxTransformTest, verifyRotateAxisAngleForDegenerateAxis) +TEST(MathUtilGfxTransformTest, verifyRotateAboutForDegenerateAxis) { // Check rotation about a degenerate zero vector. // It is expected to skip applying the rotation. gfx::Transform A; - MathUtil::rotateAxisAngle(&A, 0, 0, 0, 45); + A.RotateAbout(gfx::Vector3dF(0, 0, 0), 45); // Verify that A remains unchanged. EXPECT_TRUE(A.IsIdentity()); initializeTestMatrix(&A); - MathUtil::rotateAxisAngle(&A, 0, 0, 0, 35); + A.RotateAbout(gfx::Vector3dF(0, 0, 0), 35); // Verify that A remains unchanged. EXPECT_ROW1_EQ(10, 14, 18, 22, A); @@ -869,31 +960,31 @@ TEST(MathUtilGfxTransformTest, verifyHasPerspective) { gfx::Transform A; A.ApplyPerspectiveDepth(1); - EXPECT_TRUE(MathUtil::hasPerspective(A)); + EXPECT_TRUE(A.HasPerspective()); A.MakeIdentity(); A.ApplyPerspectiveDepth(0); - EXPECT_FALSE(MathUtil::hasPerspective(A)); + EXPECT_FALSE(A.HasPerspective()); A.MakeIdentity(); A.matrix().setDouble(3, 0, -1); - EXPECT_TRUE(MathUtil::hasPerspective(A)); + EXPECT_TRUE(A.HasPerspective()); A.MakeIdentity(); A.matrix().setDouble(3, 1, -1); - EXPECT_TRUE(MathUtil::hasPerspective(A)); + EXPECT_TRUE(A.HasPerspective()); A.MakeIdentity(); A.matrix().setDouble(3, 2, -0.3); - EXPECT_TRUE(MathUtil::hasPerspective(A)); + EXPECT_TRUE(A.HasPerspective()); A.MakeIdentity(); A.matrix().setDouble(3, 3, 0.5); - EXPECT_TRUE(MathUtil::hasPerspective(A)); + EXPECT_TRUE(A.HasPerspective()); A.MakeIdentity(); A.matrix().setDouble(3, 3, 0); - EXPECT_TRUE(MathUtil::hasPerspective(A)); + EXPECT_TRUE(A.HasPerspective()); } TEST(MathUtilGfxTransformTest, verifyIsInvertible) @@ -1033,80 +1124,166 @@ TEST(MathUtilGfxTransformTest, verifyIsIdentityOrTranslation) gfx::Transform A; initializeTestMatrix(&A); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsIdentityOrTranslation()); A.MakeIdentity(); - EXPECT_TRUE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_TRUE(A.IsIdentityOrTranslation()); - // Modifying any non-translation components should cause isIdentityOrTranslation() to + // Modifying any non-translation components should cause IsIdentityOrTranslation() to // return false. NOTE: (0, 3), (1, 3), and (2, 3) are the translation components, so - // modifying them should still return true for isIdentityOrTranslation(). + // modifying them should still return true. A.MakeIdentity(); A.matrix().setDouble(0, 0, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsIdentityOrTranslation()); A.MakeIdentity(); A.matrix().setDouble(1, 0, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsIdentityOrTranslation()); A.MakeIdentity(); A.matrix().setDouble(2, 0, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsIdentityOrTranslation()); A.MakeIdentity(); A.matrix().setDouble(3, 0, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsIdentityOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(0, 1, 2); + EXPECT_FALSE(A.IsIdentityOrTranslation()); A.MakeIdentity(); + A.matrix().setDouble(1, 1, 2); + EXPECT_FALSE(A.IsIdentityOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(2, 1, 2); + EXPECT_FALSE(A.IsIdentityOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(3, 1, 2); + EXPECT_FALSE(A.IsIdentityOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(0, 2, 2); + EXPECT_FALSE(A.IsIdentityOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(1, 2, 2); + EXPECT_FALSE(A.IsIdentityOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(2, 2, 2); + EXPECT_FALSE(A.IsIdentityOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(3, 2, 2); + EXPECT_FALSE(A.IsIdentityOrTranslation()); + + // Note carefully - expecting true here. + A.MakeIdentity(); + A.matrix().setDouble(0, 3, 2); + EXPECT_TRUE(A.IsIdentityOrTranslation()); + + // Note carefully - expecting true here. + A.MakeIdentity(); + A.matrix().setDouble(1, 3, 2); + EXPECT_TRUE(A.IsIdentityOrTranslation()); + + // Note carefully - expecting true here. + A.MakeIdentity(); + A.matrix().setDouble(2, 3, 2); + EXPECT_TRUE(A.IsIdentityOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(3, 3, 2); + EXPECT_FALSE(A.IsIdentityOrTranslation()); +} + +TEST(MathUtilGfxTransformTest, verifyIsScaleOrTranslation) +{ + gfx::Transform A; + + initializeTestMatrix(&A); + EXPECT_FALSE(A.IsScaleOrTranslation()); + + A.MakeIdentity(); + EXPECT_TRUE(A.IsScaleOrTranslation()); + + // Modifying any non-scale or non-translation components should cause + // IsScaleOrTranslation() to return false. (0, 0), (1, 1), (2, 2), (0, 3), + // (1, 3), and (2, 3) are the scale and translation components, so + // modifying them should still return true. + + // Note carefully - expecting true here. + A.MakeIdentity(); A.matrix().setDouble(0, 0, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_TRUE(A.IsScaleOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(1, 0, 2); + EXPECT_FALSE(A.IsScaleOrTranslation()); A.MakeIdentity(); + A.matrix().setDouble(2, 0, 2); + EXPECT_FALSE(A.IsScaleOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(3, 0, 2); + EXPECT_FALSE(A.IsScaleOrTranslation()); + + A.MakeIdentity(); + A.matrix().setDouble(0, 1, 2); + EXPECT_FALSE(A.IsScaleOrTranslation()); + + // Note carefully - expecting true here. + A.MakeIdentity(); A.matrix().setDouble(1, 1, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_TRUE(A.IsScaleOrTranslation()); A.MakeIdentity(); A.matrix().setDouble(2, 1, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsScaleOrTranslation()); A.MakeIdentity(); A.matrix().setDouble(3, 1, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsScaleOrTranslation()); A.MakeIdentity(); A.matrix().setDouble(0, 2, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsScaleOrTranslation()); A.MakeIdentity(); A.matrix().setDouble(1, 2, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsScaleOrTranslation()); + // Note carefully - expecting true here. A.MakeIdentity(); A.matrix().setDouble(2, 2, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_TRUE(A.IsScaleOrTranslation()); A.MakeIdentity(); A.matrix().setDouble(3, 2, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsScaleOrTranslation()); // Note carefully - expecting true here. A.MakeIdentity(); A.matrix().setDouble(0, 3, 2); - EXPECT_TRUE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_TRUE(A.IsScaleOrTranslation()); // Note carefully - expecting true here. A.MakeIdentity(); A.matrix().setDouble(1, 3, 2); - EXPECT_TRUE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_TRUE(A.IsScaleOrTranslation()); // Note carefully - expecting true here. A.MakeIdentity(); A.matrix().setDouble(2, 3, 2); - EXPECT_TRUE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_TRUE(A.IsScaleOrTranslation()); A.MakeIdentity(); A.matrix().setDouble(3, 3, 2); - EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A)); + EXPECT_FALSE(A.IsScaleOrTranslation()); } } // namespace diff --git a/cc/occlusion_tracker_unittest.cc b/cc/occlusion_tracker_unittest.cc index c46e8fd..e03e849 100644 --- a/cc/occlusion_tracker_unittest.cc +++ b/cc/occlusion_tracker_unittest.cc @@ -1934,7 +1934,7 @@ protected: gfx::Transform transform; transform.Translate(150, 150); transform.ApplyPerspectiveDepth(400); - MathUtil::rotateAxisAngle(&transform, 1, 0, 0, -30); + transform.RotateAboutXAxis(-30); transform.Translate(-150, -150); typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, gfx::PointF(0, 0), gfx::Size(300, 300)); @@ -1966,7 +1966,7 @@ protected: transform.ApplyPerspectiveDepth(10); transform.Translate(-250, -50); transform.Translate(250, 50); - MathUtil::rotateAxisAngle(&transform, 1, 0, 0, -167); + transform.RotateAboutXAxis(-167); transform.Translate(-250, -50); typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, gfx::PointF(0, 0), gfx::Size(500, 100)); diff --git a/ui/gfx/transform.cc b/ui/gfx/transform.cc index 19b59da..b75bb7f 100644 --- a/ui/gfx/transform.cc +++ b/ui/gfx/transform.cc @@ -49,34 +49,71 @@ void Transform::MakeIdentity() { matrix_.setIdentity(); } -void Transform::Rotate(double degree) { +void Transform::RotateAboutXAxis(double degrees) { + double radians = degrees * M_PI / 180; + double cosTheta = std::cos(radians); + double sinTheta = std::sin(radians); + if (matrix_.isIdentity()) { + matrix_.set3x3(1, 0, 0, + 0, cosTheta, sinTheta, + 0, -sinTheta, cosTheta); + } else { + SkMatrix44 rot; + rot.set3x3(1, 0, 0, + 0, cosTheta, sinTheta, + 0, -sinTheta, cosTheta); + matrix_.preConcat(rot); + } +} + +void Transform::RotateAboutYAxis(double degrees) { + double radians = degrees * M_PI / 180; + double cosTheta = std::cos(radians); + double sinTheta = std::sin(radians); + if (matrix_.isIdentity()) { + // Note carefully the placement of the -sinTheta for rotation about + // y-axis is different than rotation about x-axis or z-axis. + matrix_.set3x3(cosTheta, 0, -sinTheta, + 0, 1, 0, + sinTheta, 0, cosTheta); + } else { + SkMatrix44 rot; + rot.set3x3(cosTheta, 0, -sinTheta, + 0, 1, 0, + sinTheta, 0, cosTheta); + matrix_.preConcat(rot); + } +} + +void Transform::RotateAboutZAxis(double degrees) { + double radians = degrees * M_PI / 180; + double cosTheta = std::cos(radians); + double sinTheta = std::sin(radians); if (matrix_.isIdentity()) { - matrix_.setRotateDegreesAbout(SkDoubleToMScalar(0), - SkDoubleToMScalar(0), - SkDoubleToMScalar(1), - SkDoubleToMScalar(degree)); + matrix_.set3x3(cosTheta, sinTheta, 0, + -sinTheta, cosTheta, 0, + 0, 0, 1); } else { SkMatrix44 rot; - rot.setRotateDegreesAbout(SkDoubleToMScalar(0), - SkDoubleToMScalar(0), - SkDoubleToMScalar(1), - SkDoubleToMScalar(degree)); + rot.set3x3(cosTheta, sinTheta, 0, + -sinTheta, cosTheta, 0, + 0, 0, 1); matrix_.preConcat(rot); } } -void Transform::RotateAbout(const Vector3dF& axis, double degree) { +void Transform::RotateAbout(const Vector3dF& axis, double degrees) { if (matrix_.isIdentity()) { matrix_.setRotateDegreesAbout(SkDoubleToMScalar(axis.x()), SkDoubleToMScalar(axis.y()), SkDoubleToMScalar(axis.z()), - SkDoubleToMScalar(degree)); + SkDoubleToMScalar(degrees)); } else { SkMatrix44 rot; rot.setRotateDegreesAbout(SkDoubleToMScalar(axis.x()), SkDoubleToMScalar(axis.y()), SkDoubleToMScalar(axis.z()), - SkDoubleToMScalar(degree)); + SkDoubleToMScalar(degrees)); matrix_.preConcat(rot); } } @@ -185,10 +222,75 @@ bool Transform::IsIdentity() const { return matrix_.isIdentity(); } +bool Transform::IsIdentityOrTranslation() const { + bool has_no_perspective = !matrix_.getDouble(3, 0) && + !matrix_.getDouble(3, 1) && + !matrix_.getDouble(3, 2) && + (matrix_.getDouble(3, 3) == 1); + + bool has_no_rotation_or_skew = !matrix_.getDouble(0, 1) && + !matrix_.getDouble(0, 2) && + !matrix_.getDouble(1, 0) && + !matrix_.getDouble(1, 2) && + !matrix_.getDouble(2, 0) && + !matrix_.getDouble(2, 1); + + bool has_no_scale = matrix_.getDouble(0, 0) == 1 && + matrix_.getDouble(1, 1) == 1 && + matrix_.getDouble(2, 2) == 1; + + return has_no_perspective && has_no_rotation_or_skew && has_no_scale; +} + +bool Transform::IsScaleOrTranslation() const { + bool has_no_perspective = !matrix_.getDouble(3, 0) && + !matrix_.getDouble(3, 1) && + !matrix_.getDouble(3, 2) && + (matrix_.getDouble(3, 3) == 1); + + bool has_no_rotation_or_skew = !matrix_.getDouble(0, 1) && + !matrix_.getDouble(0, 2) && + !matrix_.getDouble(1, 0) && + !matrix_.getDouble(1, 2) && + !matrix_.getDouble(2, 0) && + !matrix_.getDouble(2, 1); + + return has_no_perspective && has_no_rotation_or_skew; +} + +bool Transform::HasPerspective() const { + return matrix_.getDouble(3, 0) || + matrix_.getDouble(3, 1) || + matrix_.getDouble(3, 2) || + (matrix_.getDouble(3, 3) != 1); +} + bool Transform::IsInvertible() const { return std::abs(matrix_.determinant()) > kTooSmallForDeterminant; } +bool Transform::IsBackFaceVisible() const { + // Compute whether a layer with a forward-facing normal of (0, 0, 1) would + // have its back face visible after applying the transform. + // + // This is done by transforming the normal and seeing if the resulting z + // value is positive or negative. However, note that transforming a normal + // actually requires using the inverse-transpose of the original transform. + + // TODO (shawnsingh) make this perform more efficiently - we do not + // actually need to instantiate/invert/transpose any matrices, exploiting the + // fact that we only need to transform (0, 0, 1, 0). + SkMatrix44 inverse; + bool invertible = matrix_.invert(&inverse); + + // Assume the transform does not apply if it's not invertible, so it's + // front face remains visible. + if (!invertible) + return false; + + return inverse.getDouble(2, 2) < 0; +} + bool Transform::GetInverse(Transform* transform) const { return matrix_.invert(&transform->matrix_); } diff --git a/ui/gfx/transform.h b/ui/gfx/transform.h index 52d15cf..cccbe72 100644 --- a/ui/gfx/transform.h +++ b/ui/gfx/transform.h @@ -29,13 +29,16 @@ class UI_EXPORT Transform { // Resets this transform to the identity transform. void MakeIdentity(); - // Applies the current transformation on a rotation and assigns the result + // Applies the current transformation on a 2d rotation and assigns the result // to |this|. - void Rotate(double degree); + void Rotate(double degrees) { RotateAboutZAxis(degrees); } // Applies the current transformation on an axis-angle rotation and assigns // the result to |this|. - void RotateAbout(const Vector3dF& point, double degree); + void RotateAboutXAxis(double degrees); + void RotateAboutYAxis(double degrees); + void RotateAboutZAxis(double degrees); + void RotateAbout(const Vector3dF& axis, double degrees); // Applies the current transformation on a scaling and assigns the result // to |this|. @@ -67,9 +70,23 @@ class UI_EXPORT Transform { // Returns true if this is the identity matrix. bool IsIdentity() const; + // Returns true if the matrix is either identity or pure translation. + bool IsIdentityOrTranslation() const; + + // Returns true if the matrix is has only scaling and translation components. + bool IsScaleOrTranslation() const; + + // Returns true if the matrix has any perspective component that would + // change the w-component of a homogeneous point. + bool HasPerspective() const; + // Returns true if this transform is non-singular. bool IsInvertible() const; + // Returns true if a layer with a forward-facing normal of (0, 0, 1) would + // have its back side facing frontwards after applying the transform. + bool IsBackFaceVisible() const; + // Inverts the transform which is passed in. Returns true if successful. bool GetInverse(Transform* transform) const WARN_UNUSED_RESULT; |