Commit c83d50b0 authored by vollick@chromium.org's avatar vollick@chromium.org

Interpolated rotations should end cleanly where possible.

Due to numeric issues, interpolated rotations of 90 degrees end at 'unclean' transforms. That is, they have some entries that are not 0, 1, or -1. With this patch, interpolated rotations detect when rotations should end cleanly and massage matrix entries where appropriate.

BUG=chrome-os-partner:9166
TEST=InterpolatedTransformTest.ScreenRotationEndsCleanly, InterpolatedTransformTest.MaximizeEndsCleanly

Review URL: http://codereview.chromium.org/10210002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133986 0039d316-1c4b-4281-b951-d872f2087c98
parent 389e0a48
...@@ -17,6 +17,54 @@ namespace { ...@@ -17,6 +17,54 @@ namespace {
static const float EPSILON = 1e-6f; static const float EPSILON = 1e-6f;
bool IsMultipleOfNinetyDegrees(float degrees)
{
float remainder = fabs(fmod(degrees, 90.0f));
return remainder < EPSILON || 90.0f - remainder < EPSILON;
}
// Returns false if |degrees| is not a multiple of ninety degrees or if
// |rotation| is NULL. It does not affect |rotation| in this case. Otherwise
// *rotation is set to be the appropriate sanitized rotation matrix. That is,
// the rotation matrix corresponding to |degrees| which has entries that are all
// either 0, 1 or -1.
bool MassageRotationIfMultipleOfNinetyDegrees(ui::Transform* rotation,
float degrees)
{
if (!IsMultipleOfNinetyDegrees(degrees) || !rotation)
return false;
ui::Transform transform;
SkMatrix44& m = transform.matrix();
float degrees_by_ninety = degrees / 90.0f;
int n = static_cast<int>(degrees_by_ninety > 0
? floor(degrees_by_ninety + 0.5f)
: ceil(degrees_by_ninety - 0.5f));
n %= 4;
if (n < 0)
n += 4;
// n should now be in the range [0, 3]
if (n == 1) {
m.set3x3( 0, 1, 0,
-1, 0, 0,
0, 0, 1);
} else if (n == 2) {
m.set3x3(-1, 0, 0,
0, -1, 0,
0, 0, 1);
} else if (n == 3) {
m.set3x3( 0, -1, 0,
1, 0, 0,
0, 0, 1);
}
*rotation = transform;
return true;
}
} // namespace } // namespace
namespace ui { namespace ui {
...@@ -157,7 +205,10 @@ InterpolatedRotation::~InterpolatedRotation() {} ...@@ -157,7 +205,10 @@ InterpolatedRotation::~InterpolatedRotation() {}
ui::Transform InterpolatedRotation::InterpolateButDoNotCompose(float t) const { ui::Transform InterpolatedRotation::InterpolateButDoNotCompose(float t) const {
ui::Transform result; ui::Transform result;
result.SetRotate(ValueBetween(t, start_degrees_, end_degrees_)); float interpolated_degrees = ValueBetween(t, start_degrees_, end_degrees_);
result.SetRotate(interpolated_degrees);
if (t == 0.0f || t == 1.0f)
MassageRotationIfMultipleOfNinetyDegrees(&result, interpolated_degrees);
return result; return result;
} }
...@@ -268,7 +319,6 @@ InterpolatedTranslation::InterpolateButDoNotCompose(float t) const { ...@@ -268,7 +319,6 @@ InterpolatedTranslation::InterpolateButDoNotCompose(float t) const {
// TODO(vollick) 3d xforms. // TODO(vollick) 3d xforms.
result.SetTranslate(ValueBetween(t, start_pos_.x(), end_pos_.x()), result.SetTranslate(ValueBetween(t, start_pos_.x(), end_pos_.x()),
ValueBetween(t, start_pos_.y(), end_pos_.y())); ValueBetween(t, start_pos_.y(), end_pos_.y()));
return result; return result;
} }
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/rect.h"
namespace { namespace {
...@@ -137,3 +138,123 @@ TEST(InterpolatedTransformTest, FactorTRS) { ...@@ -137,3 +138,123 @@ TEST(InterpolatedTransformTest, FactorTRS) {
EXPECT_FLOAT_EQ(scale.y(), 2 * degrees + 1); EXPECT_FLOAT_EQ(scale.y(), 2 * degrees + 1);
} }
} }
ui::InterpolatedTransform* GetScreenRotation(int degrees, bool reversed) {
gfx::Point old_pivot;
gfx::Point new_pivot;
int width = 1920;
int height = 180;
switch (degrees) {
case 90:
new_pivot = gfx::Point(width, 0);
break;
case -90:
new_pivot = gfx::Point(0, height);
break;
case 180:
case 360:
new_pivot = old_pivot = gfx::Point(width / 2, height / 2);
break;
}
scoped_ptr<ui::InterpolatedTransform> rotation(
new ui::InterpolatedTransformAboutPivot(
old_pivot,
new ui::InterpolatedRotation(reversed ? degrees : 0,
reversed ? 0 : degrees)));
scoped_ptr<ui::InterpolatedTransform> translation(
new ui::InterpolatedTranslation(
gfx::Point(0, 0),
gfx::Point(new_pivot.x() - old_pivot.x(),
new_pivot.y() - old_pivot.y())));
float scale_factor = 0.9f;
scoped_ptr<ui::InterpolatedTransform> scale_down(
new ui::InterpolatedScale(1.0f, scale_factor, 0.0f, 0.5f));
scoped_ptr<ui::InterpolatedTransform> scale_up(
new ui::InterpolatedScale(1.0f, 1.0f / scale_factor, 0.5f, 1.0f));
scoped_ptr<ui::InterpolatedTransform> to_return(
new ui::InterpolatedConstantTransform(ui::Transform()));
scale_up->SetChild(scale_down.release());
translation->SetChild(scale_up.release());
rotation->SetChild(translation.release());
to_return->SetChild(rotation.release());
to_return->SetReversed(reversed);
return to_return.release();
}
TEST(InterpolatedTransformTest, ScreenRotationEndsCleanly) {
for (int i = 0; i < 2; ++i) {
for (int degrees = -360; degrees <= 360; degrees += 90) {
const bool reversed = i == 1;
scoped_ptr<ui::InterpolatedTransform> screen_rotation(
GetScreenRotation(degrees, reversed));
ui::Transform interpolated = screen_rotation->Interpolate(1.0f);
SkMatrix44& m = interpolated.matrix();
// Upper-left 3x3 matrix should all be 0, 1 or -1.
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 3; ++col) {
float entry = m.get(row, col);
EXPECT_TRUE(entry == 0 || entry == 1 || entry == -1);
}
}
}
}
}
ui::InterpolatedTransform* GetMaximize() {
gfx::Rect target_bounds(0, 0, 1920, 1080);
gfx::Rect initial_bounds(30, 1000, 192, 108);
float scale_x = static_cast<float>(
target_bounds.height()) / initial_bounds.width();
float scale_y = static_cast<float>(
target_bounds.width()) / initial_bounds.height();
scoped_ptr<ui::InterpolatedTransform> scale(
new ui::InterpolatedScale(gfx::Point3f(1, 1, 1),
gfx::Point3f(scale_x, scale_y, 1)));
scoped_ptr<ui::InterpolatedTransform> translation(
new ui::InterpolatedTranslation(
gfx::Point(),
gfx::Point(target_bounds.x() - initial_bounds.x(),
target_bounds.y() - initial_bounds.y())));
scoped_ptr<ui::InterpolatedTransform> rotation(
new ui::InterpolatedRotation(0, 4.0f));
scoped_ptr<ui::InterpolatedTransform> rotation_about_pivot(
new ui::InterpolatedTransformAboutPivot(
gfx::Point(initial_bounds.width() * 0.5,
initial_bounds.height() * 0.5),
rotation.release()));
scale->SetChild(translation.release());
rotation_about_pivot->SetChild(scale.release());
rotation_about_pivot->SetReversed(true);
return rotation_about_pivot.release();
}
TEST(InterpolatedTransformTest, MaximizeEndsCleanly) {
scoped_ptr<ui::InterpolatedTransform> maximize(GetMaximize());
ui::Transform interpolated = maximize->Interpolate(1.0f);
SkMatrix44& m = interpolated.matrix();
// Upper-left 3x3 matrix should all be 0, 1 or -1.
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 3; ++col) {
float entry = m.get(row, col);
EXPECT_TRUE(entry == 0 || entry == 1 || entry == -1);
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment