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