summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshawnsingh@google.com <shawnsingh@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-28 23:49:26 +0000
committershawnsingh@google.com <shawnsingh@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-28 23:49:26 +0000
commit2c7cd6d2b9b4180cbf8f5067cf9c21e3c8051b73 (patch)
treefc3598a693c1649888f42c14485cfaa8c8c448ba
parentf657203ad9a1a67d54d507652b613e80f9f80a15 (diff)
downloadchromium_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.cc6
-rw-r--r--cc/layer_tree_host_common.cc18
-rw-r--r--cc/layer_tree_host_common_unittest.cc8
-rw-r--r--cc/math_util.cc63
-rw-r--r--cc/math_util.h4
-rw-r--r--cc/math_util_unittest.cc267
-rw-r--r--cc/occlusion_tracker_unittest.cc4
-rw-r--r--ui/gfx/transform.cc126
-rw-r--r--ui/gfx/transform.h23
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;