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