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
This commit is contained in:
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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, 0, 2);
|
||||
EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A));
|
||||
A.matrix().setDouble(0, 1, 2);
|
||||
EXPECT_FALSE(A.IsIdentityOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(1, 1, 2);
|
||||
EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_FALSE(A.IsIdentityOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(2, 1, 2);
|
||||
EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_FALSE(A.IsIdentityOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(3, 1, 2);
|
||||
EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_FALSE(A.IsIdentityOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(0, 2, 2);
|
||||
EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_FALSE(A.IsIdentityOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(1, 2, 2);
|
||||
EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_FALSE(A.IsIdentityOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(2, 2, 2);
|
||||
EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_FALSE(A.IsIdentityOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(3, 2, 2);
|
||||
EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_FALSE(A.IsIdentityOrTranslation());
|
||||
|
||||
// Note carefully - expecting true here.
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(0, 3, 2);
|
||||
EXPECT_TRUE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_TRUE(A.IsIdentityOrTranslation());
|
||||
|
||||
// Note carefully - expecting true here.
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(1, 3, 2);
|
||||
EXPECT_TRUE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_TRUE(A.IsIdentityOrTranslation());
|
||||
|
||||
// Note carefully - expecting true here.
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(2, 3, 2);
|
||||
EXPECT_TRUE(MathUtil::isIdentityOrTranslation(A));
|
||||
EXPECT_TRUE(A.IsIdentityOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(3, 3, 2);
|
||||
EXPECT_FALSE(MathUtil::isIdentityOrTranslation(A));
|
||||
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_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_TRUE(A.IsScaleOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(2, 1, 2);
|
||||
EXPECT_FALSE(A.IsScaleOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(3, 1, 2);
|
||||
EXPECT_FALSE(A.IsScaleOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(0, 2, 2);
|
||||
EXPECT_FALSE(A.IsScaleOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(1, 2, 2);
|
||||
EXPECT_FALSE(A.IsScaleOrTranslation());
|
||||
|
||||
// Note carefully - expecting true here.
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(2, 2, 2);
|
||||
EXPECT_TRUE(A.IsScaleOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(3, 2, 2);
|
||||
EXPECT_FALSE(A.IsScaleOrTranslation());
|
||||
|
||||
// Note carefully - expecting true here.
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(0, 3, 2);
|
||||
EXPECT_TRUE(A.IsScaleOrTranslation());
|
||||
|
||||
// Note carefully - expecting true here.
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(1, 3, 2);
|
||||
EXPECT_TRUE(A.IsScaleOrTranslation());
|
||||
|
||||
// Note carefully - expecting true here.
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(2, 3, 2);
|
||||
EXPECT_TRUE(A.IsScaleOrTranslation());
|
||||
|
||||
A.MakeIdentity();
|
||||
A.matrix().setDouble(3, 3, 2);
|
||||
EXPECT_FALSE(A.IsScaleOrTranslation());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -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));
|
||||
|
@ -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_.setRotateDegreesAbout(SkDoubleToMScalar(0),
|
||||
SkDoubleToMScalar(0),
|
||||
SkDoubleToMScalar(1),
|
||||
SkDoubleToMScalar(degree));
|
||||
matrix_.set3x3(1, 0, 0,
|
||||
0, cosTheta, sinTheta,
|
||||
0, -sinTheta, cosTheta);
|
||||
} else {
|
||||
SkMatrix44 rot;
|
||||
rot.setRotateDegreesAbout(SkDoubleToMScalar(0),
|
||||
SkDoubleToMScalar(0),
|
||||
SkDoubleToMScalar(1),
|
||||
SkDoubleToMScalar(degree));
|
||||
rot.set3x3(1, 0, 0,
|
||||
0, cosTheta, sinTheta,
|
||||
0, -sinTheta, cosTheta);
|
||||
matrix_.preConcat(rot);
|
||||
}
|
||||
}
|
||||
|
||||
void Transform::RotateAbout(const Vector3dF& axis, double degree) {
|
||||
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_.set3x3(cosTheta, sinTheta, 0,
|
||||
-sinTheta, cosTheta, 0,
|
||||
0, 0, 1);
|
||||
} else {
|
||||
SkMatrix44 rot;
|
||||
rot.set3x3(cosTheta, sinTheta, 0,
|
||||
-sinTheta, cosTheta, 0,
|
||||
0, 0, 1);
|
||||
matrix_.preConcat(rot);
|
||||
}
|
||||
}
|
||||
|
||||
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_);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user