Home | History | Annotate | Download | only in gfx
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/gfx/interpolated_transform.h"
      6 
      7 #include <cmath>
      8 
      9 #ifndef M_PI
     10 #define M_PI 3.14159265358979323846
     11 #endif
     12 
     13 #include "base/logging.h"
     14 #include "ui/gfx/animation/tween.h"
     15 
     16 namespace {
     17 
     18 static const double EPSILON = 1e-6;
     19 
     20 bool IsMultipleOfNinetyDegrees(double degrees) {
     21   double remainder = fabs(fmod(degrees, 90.0));
     22   return remainder < EPSILON || 90.0 - remainder < EPSILON;
     23 }
     24 
     25 // Returns false if |degrees| is not a multiple of ninety degrees or if
     26 // |rotation| is NULL. It does not affect |rotation| in this case. Otherwise
     27 // *rotation is set to be the appropriate sanitized rotation matrix. That is,
     28 // the rotation matrix corresponding to |degrees| which has entries that are all
     29 // either 0, 1 or -1.
     30 bool MassageRotationIfMultipleOfNinetyDegrees(gfx::Transform* rotation,
     31                                               float degrees) {
     32   if (!IsMultipleOfNinetyDegrees(degrees) || !rotation)
     33     return false;
     34 
     35   gfx::Transform transform;
     36   SkMatrix44& m = transform.matrix();
     37   float degrees_by_ninety = degrees / 90.0f;
     38 
     39   int n = static_cast<int>(degrees_by_ninety > 0
     40       ? floor(degrees_by_ninety + 0.5f)
     41       : ceil(degrees_by_ninety - 0.5f));
     42 
     43   n %= 4;
     44   if (n < 0)
     45     n += 4;
     46 
     47   // n should now be in the range [0, 3]
     48   if (n == 1) {
     49     m.set3x3( 0,  1,  0,
     50              -1,  0,  0,
     51               0,  0,  1);
     52   } else if (n == 2) {
     53     m.set3x3(-1,  0,  0,
     54               0, -1,  0,
     55               0,  0,  1);
     56   } else if (n == 3) {
     57     m.set3x3( 0, -1,  0,
     58               1,  0,  0,
     59               0,  0,  1);
     60   }
     61 
     62   *rotation = transform;
     63   return true;
     64 }
     65 
     66 } // namespace
     67 
     68 namespace ui {
     69 
     70 ///////////////////////////////////////////////////////////////////////////////
     71 // InterpolatedTransform
     72 //
     73 
     74 InterpolatedTransform::InterpolatedTransform()
     75     : start_time_(0.0f),
     76       end_time_(1.0f),
     77       reversed_(false) {
     78 }
     79 
     80 InterpolatedTransform::InterpolatedTransform(float start_time,
     81                                              float end_time)
     82     : start_time_(start_time),
     83       end_time_(end_time),
     84       reversed_(false) {
     85 }
     86 
     87 InterpolatedTransform::~InterpolatedTransform() {}
     88 
     89 gfx::Transform InterpolatedTransform::Interpolate(float t) const {
     90   if (reversed_)
     91     t = 1.0f - t;
     92   gfx::Transform result = InterpolateButDoNotCompose(t);
     93   if (child_.get()) {
     94     result.ConcatTransform(child_->Interpolate(t));
     95   }
     96   return result;
     97 }
     98 
     99 void InterpolatedTransform::SetChild(InterpolatedTransform* child) {
    100   child_.reset(child);
    101 }
    102 
    103 inline float InterpolatedTransform::ValueBetween(float time,
    104                                                  float start_value,
    105                                                  float end_value) const {
    106   // can't handle NaN
    107   DCHECK(time == time && start_time_ == start_time_ && end_time_ == end_time_);
    108   if (time != time || start_time_ != start_time_ || end_time_ != end_time_)
    109     return start_value;
    110 
    111   // Ok if equal -- we'll get a step function. Note: if end_time_ ==
    112   // start_time_ == x, then if none of the numbers are NaN, then it
    113   // must be true that time < x or time >= x, so we will return early
    114   // due to one of the following if statements.
    115   DCHECK(end_time_ >= start_time_);
    116 
    117   if (time < start_time_)
    118     return start_value;
    119 
    120   if (time >= end_time_)
    121     return end_value;
    122 
    123   float t = (time - start_time_) / (end_time_ - start_time_);
    124   return static_cast<float>(
    125       gfx::Tween::DoubleValueBetween(t, start_value, end_value));
    126 }
    127 
    128 ///////////////////////////////////////////////////////////////////////////////
    129 // InterpolatedRotation
    130 //
    131 
    132 InterpolatedRotation::InterpolatedRotation(float start_degrees,
    133                                            float end_degrees)
    134     : InterpolatedTransform(),
    135       start_degrees_(start_degrees),
    136       end_degrees_(end_degrees) {
    137 }
    138 
    139 InterpolatedRotation::InterpolatedRotation(float start_degrees,
    140                                            float end_degrees,
    141                                            float start_time,
    142                                            float end_time)
    143     : InterpolatedTransform(start_time, end_time),
    144       start_degrees_(start_degrees),
    145       end_degrees_(end_degrees) {
    146 }
    147 
    148 InterpolatedRotation::~InterpolatedRotation() {}
    149 
    150 gfx::Transform InterpolatedRotation::InterpolateButDoNotCompose(float t) const {
    151   gfx::Transform result;
    152   float interpolated_degrees = ValueBetween(t, start_degrees_, end_degrees_);
    153   result.Rotate(interpolated_degrees);
    154   if (t == 0.0f || t == 1.0f)
    155     MassageRotationIfMultipleOfNinetyDegrees(&result, interpolated_degrees);
    156   return result;
    157 }
    158 
    159 ///////////////////////////////////////////////////////////////////////////////
    160 // InterpolatedAxisAngleRotation
    161 //
    162 
    163 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
    164     const gfx::Vector3dF& axis,
    165     float start_degrees,
    166     float end_degrees)
    167     : InterpolatedTransform(),
    168       axis_(axis),
    169       start_degrees_(start_degrees),
    170       end_degrees_(end_degrees) {
    171 }
    172 
    173 InterpolatedAxisAngleRotation::InterpolatedAxisAngleRotation(
    174     const gfx::Vector3dF& axis,
    175     float start_degrees,
    176     float end_degrees,
    177     float start_time,
    178     float end_time)
    179     : InterpolatedTransform(start_time, end_time),
    180       axis_(axis),
    181       start_degrees_(start_degrees),
    182       end_degrees_(end_degrees) {
    183 }
    184 
    185 InterpolatedAxisAngleRotation::~InterpolatedAxisAngleRotation() {}
    186 
    187 gfx::Transform
    188 InterpolatedAxisAngleRotation::InterpolateButDoNotCompose(float t) const {
    189   gfx::Transform result;
    190   result.RotateAbout(axis_, ValueBetween(t, start_degrees_, end_degrees_));
    191   return result;
    192 }
    193 
    194 ///////////////////////////////////////////////////////////////////////////////
    195 // InterpolatedScale
    196 //
    197 
    198 InterpolatedScale::InterpolatedScale(float start_scale, float end_scale)
    199     : InterpolatedTransform(),
    200       start_scale_(gfx::Point3F(start_scale, start_scale, start_scale)),
    201       end_scale_(gfx::Point3F(end_scale, end_scale, end_scale)) {
    202 }
    203 
    204 InterpolatedScale::InterpolatedScale(float start_scale, float end_scale,
    205                                      float start_time, float end_time)
    206     : InterpolatedTransform(start_time, end_time),
    207       start_scale_(gfx::Point3F(start_scale, start_scale, start_scale)),
    208       end_scale_(gfx::Point3F(end_scale, end_scale, end_scale)) {
    209 }
    210 
    211 InterpolatedScale::InterpolatedScale(const gfx::Point3F& start_scale,
    212                                      const gfx::Point3F& end_scale)
    213     : InterpolatedTransform(),
    214       start_scale_(start_scale),
    215       end_scale_(end_scale) {
    216 }
    217 
    218 InterpolatedScale::InterpolatedScale(const gfx::Point3F& start_scale,
    219                                      const gfx::Point3F& end_scale,
    220                                      float start_time,
    221                                      float end_time)
    222     : InterpolatedTransform(start_time, end_time),
    223       start_scale_(start_scale),
    224       end_scale_(end_scale) {
    225 }
    226 
    227 InterpolatedScale::~InterpolatedScale() {}
    228 
    229 gfx::Transform InterpolatedScale::InterpolateButDoNotCompose(float t) const {
    230   gfx::Transform result;
    231   float scale_x = ValueBetween(t, start_scale_.x(), end_scale_.x());
    232   float scale_y = ValueBetween(t, start_scale_.y(), end_scale_.y());
    233   // TODO(vollick) 3d xforms.
    234   result.Scale(scale_x, scale_y);
    235   return result;
    236 }
    237 
    238 ///////////////////////////////////////////////////////////////////////////////
    239 // InterpolatedTranslation
    240 //
    241 
    242 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point& start_pos,
    243                                                  const gfx::Point& end_pos)
    244     : InterpolatedTransform(),
    245       start_pos_(start_pos),
    246       end_pos_(end_pos) {
    247 }
    248 
    249 InterpolatedTranslation::InterpolatedTranslation(const gfx::Point& start_pos,
    250                                                  const gfx::Point& end_pos,
    251                                                  float start_time,
    252                                                  float end_time)
    253     : InterpolatedTransform(start_time, end_time),
    254       start_pos_(start_pos),
    255       end_pos_(end_pos) {
    256 }
    257 
    258 InterpolatedTranslation::~InterpolatedTranslation() {}
    259 
    260 gfx::Transform
    261 InterpolatedTranslation::InterpolateButDoNotCompose(float t) const {
    262   gfx::Transform result;
    263   // TODO(vollick) 3d xforms.
    264   result.Translate(ValueBetween(t, start_pos_.x(), end_pos_.x()),
    265                       ValueBetween(t, start_pos_.y(), end_pos_.y()));
    266   return result;
    267 }
    268 
    269 ///////////////////////////////////////////////////////////////////////////////
    270 // InterpolatedConstantTransform
    271 //
    272 
    273 InterpolatedConstantTransform::InterpolatedConstantTransform(
    274   const gfx::Transform& transform)
    275     : InterpolatedTransform(),
    276   transform_(transform) {
    277 }
    278 
    279 gfx::Transform
    280 InterpolatedConstantTransform::InterpolateButDoNotCompose(float t) const {
    281   return transform_;
    282 }
    283 
    284 InterpolatedConstantTransform::~InterpolatedConstantTransform() {}
    285 
    286 ///////////////////////////////////////////////////////////////////////////////
    287 // InterpolatedTransformAboutPivot
    288 //
    289 
    290 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
    291   const gfx::Point& pivot,
    292   InterpolatedTransform* transform)
    293     : InterpolatedTransform() {
    294   Init(pivot, transform);
    295 }
    296 
    297 InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
    298   const gfx::Point& pivot,
    299   InterpolatedTransform* transform,
    300   float start_time,
    301   float end_time)
    302     : InterpolatedTransform() {
    303   Init(pivot, transform);
    304 }
    305 
    306 InterpolatedTransformAboutPivot::~InterpolatedTransformAboutPivot() {}
    307 
    308 gfx::Transform
    309 InterpolatedTransformAboutPivot::InterpolateButDoNotCompose(float t) const {
    310   if (transform_.get()) {
    311     return transform_->Interpolate(t);
    312   }
    313   return gfx::Transform();
    314 }
    315 
    316 void InterpolatedTransformAboutPivot::Init(const gfx::Point& pivot,
    317                                            InterpolatedTransform* xform) {
    318   gfx::Transform to_pivot;
    319   gfx::Transform from_pivot;
    320   to_pivot.Translate(-pivot.x(), -pivot.y());
    321   from_pivot.Translate(pivot.x(), pivot.y());
    322 
    323   scoped_ptr<InterpolatedTransform> pre_transform(
    324     new InterpolatedConstantTransform(to_pivot));
    325   scoped_ptr<InterpolatedTransform> post_transform(
    326     new InterpolatedConstantTransform(from_pivot));
    327 
    328   pre_transform->SetChild(xform);
    329   xform->SetChild(post_transform.release());
    330   transform_.reset(pre_transform.release());
    331 }
    332 
    333 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
    334     const gfx::Transform& start_transform,
    335     const gfx::Transform& end_transform)
    336     : InterpolatedTransform() {
    337   Init(start_transform, end_transform);
    338 }
    339 
    340 InterpolatedMatrixTransform::InterpolatedMatrixTransform(
    341     const gfx::Transform& start_transform,
    342     const gfx::Transform& end_transform,
    343     float start_time,
    344     float end_time)
    345     : InterpolatedTransform() {
    346   Init(start_transform, end_transform);
    347 }
    348 
    349 InterpolatedMatrixTransform::~InterpolatedMatrixTransform() {}
    350 
    351 gfx::Transform
    352 InterpolatedMatrixTransform::InterpolateButDoNotCompose(float t) const {
    353   gfx::DecomposedTransform blended;
    354   bool success = gfx::BlendDecomposedTransforms(&blended,
    355                                                 end_decomp_,
    356                                                 start_decomp_,
    357                                                 t);
    358   DCHECK(success);
    359   return gfx::ComposeTransform(blended);
    360 }
    361 
    362 void InterpolatedMatrixTransform::Init(const gfx::Transform& start_transform,
    363                                        const gfx::Transform& end_transform) {
    364   bool success = gfx::DecomposeTransform(&start_decomp_, start_transform);
    365   DCHECK(success);
    366   success = gfx::DecomposeTransform(&end_decomp_, end_transform);
    367   DCHECK(success);
    368 }
    369 
    370 } // namespace ui
    371