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