Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 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 <cmath> | |
| 6 | |
| 7 #include "base/lazy_instance.h" | |
| 8 #include "ui/gfx/android/scroller.h" | |
| 9 #include "ui/gfx/android/view_configuration.h" | |
| 10 | |
| 11 namespace gfx { | |
| 12 namespace { | |
| 13 | |
| 14 const base::TimeDelta kDefaultDuration = base::TimeDelta::FromMilliseconds(250); | |
|
aelias_OOO_until_Jul13
2014/02/21 02:42:35
Hmm, I'm concerned these will end up as static ini
jdduke (slow)
2014/02/21 23:33:02
I'll just compute the constant in kDecelerationRat
| |
| 15 | |
| 16 const float kDecelerationRate = std::log(0.78f) / std::log(0.9f); | |
| 17 | |
| 18 // Tension lines cross at (kInflexion, 1) | |
| 19 const float kInflexion = 0.35f; | |
| 20 | |
| 21 const float kEpsilon = 1e-5; | |
|
aelias_OOO_until_Jul13
2014/02/21 02:42:35
1e-5f instead?
jdduke (slow)
2014/02/21 23:33:02
Done.
| |
| 22 | |
| 23 bool ApproxEquals(float a, float b) { | |
| 24 return std::abs(a - b) < kEpsilon; | |
| 25 } | |
| 26 | |
| 27 struct ViscosityConstants { | |
| 28 ViscosityConstants() | |
| 29 : viscous_fluid_scale_(8.f), viscous_fluid_normalize_(1.f) { | |
| 30 viscous_fluid_normalize_ = 1.0f / ApplyViscosity(1.0f); | |
| 31 } | |
| 32 | |
| 33 float ApplyViscosity(float x) { | |
| 34 x *= viscous_fluid_scale_; | |
| 35 if (x < 1.0f) { | |
| 36 x -= (1.0f - std::exp(-x)); | |
| 37 } else { | |
| 38 float start = 0.36787944117f; // 1/e == exp(-1) | |
| 39 x = 1.0f - std::exp(1.0f - x); | |
| 40 x = start + x * (1.0f - start); | |
| 41 } | |
| 42 x *= viscous_fluid_normalize_; | |
| 43 return x; | |
| 44 } | |
| 45 | |
| 46 private: | |
| 47 // This controls the viscous fluid effect (how much of it) | |
| 48 float viscous_fluid_scale_; | |
| 49 float viscous_fluid_normalize_; | |
| 50 | |
| 51 DISALLOW_COPY_AND_ASSIGN(ViscosityConstants); | |
| 52 }; | |
| 53 | |
| 54 struct SplineConstants { | |
| 55 SplineConstants() { | |
| 56 const float kStartTension = 0.5f; | |
| 57 const float kEndTension = 1.0f; | |
| 58 const float kP1 = kStartTension * kInflexion; | |
| 59 const float kP2 = 1.0f - kEndTension * (1.0f - kInflexion); | |
| 60 | |
| 61 float x_min = 0.0f; | |
| 62 float y_min = 0.0f; | |
| 63 for (int i = 0; i < NUM_SAMPLES; i++) { | |
| 64 const float alpha = static_cast<float>(i) / NUM_SAMPLES; | |
| 65 | |
| 66 float x_max = 1.0f; | |
| 67 float x, tx, coef; | |
| 68 while (true) { | |
| 69 x = x_min + (x_max - x_min) / 2.0f; | |
| 70 coef = 3.0f * x * (1.0f - x); | |
| 71 tx = coef * ((1.0f - x) * kP1 + x * kP2) + x * x * x; | |
| 72 if (std::abs(tx - alpha) < 1E-5) | |
|
aelias_OOO_until_Jul13
2014/02/21 02:42:35
kEpsilon?
jdduke (slow)
2014/02/21 23:33:02
Done.
| |
| 73 break; | |
| 74 if (tx > alpha) | |
| 75 x_max = x; | |
| 76 else | |
| 77 x_min = x; | |
| 78 } | |
| 79 spline_position_[i] = coef * ((1.0f - x) * kStartTension + x) + x * x * x; | |
| 80 | |
| 81 float y_max = 1.0f; | |
| 82 float y, dy; | |
| 83 while (true) { | |
| 84 y = y_min + (y_max - y_min) / 2.0f; | |
| 85 coef = 3.0f * y * (1.0f - y); | |
| 86 dy = coef * ((1.0f - y) * kStartTension + y) + y * y * y; | |
| 87 if (std::abs(dy - alpha) < 1E-5) | |
|
aelias_OOO_until_Jul13
2014/02/21 02:42:35
kEpsilon?
jdduke (slow)
2014/02/21 23:33:02
Done.
| |
| 88 break; | |
| 89 if (dy > alpha) | |
| 90 y_max = y; | |
| 91 else | |
| 92 y_min = y; | |
| 93 } | |
| 94 spline_time_[i] = coef * ((1.0f - y) * kP1 + y * kP2) + y * y * y; | |
| 95 } | |
| 96 spline_position_[NUM_SAMPLES] = spline_time_[NUM_SAMPLES] = 1.0f; | |
| 97 } | |
| 98 | |
| 99 void CalculateCoefficients(float t, | |
| 100 float* distance_coef, | |
| 101 float* velocity_coef) { | |
| 102 *distance_coef = 1.f; | |
| 103 *velocity_coef = 0.f; | |
| 104 const int index = static_cast<int>(NUM_SAMPLES * t); | |
| 105 if (index < NUM_SAMPLES) { | |
| 106 const float t_inf = static_cast<float>(index) / NUM_SAMPLES; | |
| 107 const float t_sup = static_cast<float>(index + 1) / NUM_SAMPLES; | |
| 108 const float d_inf = spline_position_[index]; | |
| 109 const float d_sup = spline_position_[index + 1]; | |
| 110 *velocity_coef = (d_sup - d_inf) / (t_sup - t_inf); | |
| 111 *distance_coef = d_inf + (t - t_inf) * *velocity_coef; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 private: | |
| 116 enum { | |
| 117 NUM_SAMPLES = 100 | |
| 118 }; | |
| 119 | |
| 120 float spline_position_[NUM_SAMPLES + 1]; | |
| 121 float spline_time_[NUM_SAMPLES + 1]; | |
| 122 | |
| 123 DISALLOW_COPY_AND_ASSIGN(SplineConstants); | |
| 124 }; | |
| 125 | |
| 126 float ComputeDeceleration(float friction) { | |
| 127 const float kGravityEarth = 9.80665f; | |
| 128 return kGravityEarth // g (m/s^2) | |
| 129 * 39.37f // inch/meter | |
| 130 * 160.f // pixels/inch | |
| 131 * friction; | |
| 132 } | |
| 133 | |
| 134 template <typename T> | |
| 135 int Signum(T t) { | |
| 136 return (T(0) < t) - (t < T(0)); | |
| 137 } | |
| 138 | |
| 139 template <typename T> | |
| 140 T Clamped(T t, T a, T b) { | |
| 141 return t < a ? a : (t > b ? b : t); | |
| 142 } | |
| 143 | |
| 144 // Leaky to allow access from the impl thread. | |
| 145 base::LazyInstance<ViscosityConstants>::Leaky g_viscosity_constants = | |
| 146 LAZY_INSTANCE_INITIALIZER; | |
| 147 | |
| 148 base::LazyInstance<SplineConstants>::Leaky g_spline_constants = | |
| 149 LAZY_INSTANCE_INITIALIZER; | |
| 150 | |
| 151 } // namespace | |
| 152 | |
| 153 Scroller::Scroller(bool enable_flywheel) | |
| 154 : mode_(UNDEFINED), | |
| 155 start_x_(0), | |
| 156 start_y_(0), | |
| 157 final_x_(0), | |
| 158 final_y_(0), | |
| 159 min_x_(0), | |
| 160 max_x_(0), | |
| 161 min_y_(0), | |
| 162 max_y_(0), | |
| 163 curr_x_(0), | |
| 164 curr_y_(0), | |
| 165 duration_seconds_reciprocal_(1), | |
| 166 delta_x_(0), | |
| 167 delta_x_norm_(1), | |
| 168 delta_y_(0), | |
| 169 delta_y_norm_(1), | |
| 170 finished_(true), | |
| 171 flywheel_enabled_(enable_flywheel), | |
| 172 velocity_(0), | |
| 173 curr_velocity_(0), | |
| 174 distance_(0), | |
| 175 fling_friction_(ViewConfiguration::GetScrollFriction()), | |
| 176 deceleration_(ComputeDeceleration(fling_friction_)), | |
| 177 tuning_coeff_(ComputeDeceleration(0.84f)) {} | |
| 178 | |
| 179 Scroller::~Scroller() {} | |
| 180 | |
| 181 void Scroller::StartScroll(float start_x, | |
| 182 float start_y, | |
| 183 float dx, | |
| 184 float dy, | |
| 185 base::TimeTicks start_time) { | |
| 186 StartScroll(start_x, start_y, dx, dy, start_time, kDefaultDuration); | |
| 187 } | |
| 188 | |
| 189 void Scroller::StartScroll(float start_x, | |
| 190 float start_y, | |
| 191 float dx, | |
| 192 float dy, | |
| 193 base::TimeTicks start_time, | |
| 194 base::TimeDelta duration) { | |
| 195 mode_ = SCROLL_MODE; | |
| 196 finished_ = false; | |
| 197 duration_ = duration; | |
| 198 duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); | |
| 199 start_time_ = start_time; | |
| 200 start_x_ = start_x; | |
| 201 start_y_ = start_y; | |
| 202 final_x_ = start_x + dx; | |
| 203 final_y_ = start_y + dy; | |
| 204 RecomputeDeltas(); | |
| 205 curr_time_ = start_time_; | |
| 206 } | |
| 207 | |
| 208 void Scroller::Fling(float start_x, | |
| 209 float start_y, | |
| 210 float velocity_x, | |
| 211 float velocity_y, | |
| 212 float min_x, | |
| 213 float max_x, | |
| 214 float min_y, | |
| 215 float max_y, | |
| 216 base::TimeTicks start_time) { | |
| 217 // Continue a scroll or fling in progress | |
| 218 if (flywheel_enabled_ && !finished_) { | |
| 219 float old_velocity_x = GetCurrVelocityX(); | |
| 220 float old_velocity_y = GetCurrVelocityY(); | |
| 221 if (Signum(velocity_x) == Signum(old_velocity_x) && | |
| 222 Signum(velocity_y) == Signum(old_velocity_y)) { | |
| 223 velocity_x += old_velocity_x; | |
| 224 velocity_y += old_velocity_y; | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 mode_ = FLING_MODE; | |
| 229 finished_ = false; | |
| 230 | |
| 231 float velocity = std::sqrt(velocity_x * velocity_x + velocity_y * velocity_y); | |
| 232 | |
| 233 velocity_ = velocity; | |
| 234 duration_ = GetSplineFlingDuration(velocity); | |
| 235 duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); | |
| 236 start_time_ = start_time; | |
| 237 curr_time_ = start_time_; | |
| 238 start_x_ = start_x; | |
| 239 start_y_ = start_y; | |
| 240 | |
| 241 float coeff_x = velocity == 0 ? 1.0f : velocity_x / velocity; | |
| 242 float coeff_y = velocity == 0 ? 1.0f : velocity_y / velocity; | |
| 243 | |
| 244 double total_distance = GetSplineFlingDistance(velocity); | |
| 245 distance_ = total_distance * Signum(velocity); | |
| 246 | |
| 247 min_x_ = min_x; | |
| 248 max_x_ = max_x; | |
| 249 min_y_ = min_y; | |
| 250 max_y_ = max_y; | |
| 251 | |
| 252 final_x_ = start_x + total_distance * coeff_x; | |
| 253 final_x_ = Clamped(final_x_, min_x_, max_x_); | |
| 254 | |
| 255 final_y_ = start_y + total_distance * coeff_y; | |
| 256 final_y_ = Clamped(final_y_, min_y_, max_y_); | |
| 257 | |
| 258 RecomputeDeltas(); | |
| 259 } | |
| 260 | |
| 261 bool Scroller::ComputeScrollOffset(base::TimeTicks time) { | |
| 262 if (finished_) | |
| 263 return false; | |
| 264 | |
| 265 base::TimeDelta time_passed = time - start_time_; | |
| 266 | |
| 267 if (time_passed < base::TimeDelta()) { | |
| 268 time_passed = base::TimeDelta(); | |
| 269 } | |
| 270 | |
| 271 if (time_passed >= duration_) { | |
| 272 curr_x_ = final_x_; | |
| 273 curr_y_ = final_y_; | |
| 274 curr_time_ = start_time_ + duration_; | |
| 275 finished_ = true; | |
| 276 return true; | |
| 277 } | |
| 278 | |
| 279 curr_time_ = time; | |
| 280 | |
| 281 const float t = time_passed.InSecondsF() * duration_seconds_reciprocal_; | |
| 282 | |
| 283 switch (mode_) { | |
| 284 case UNDEFINED: | |
| 285 NOTREACHED() << "Invalid scroll mode when computing scroll offset."; | |
| 286 return false; | |
| 287 | |
| 288 case SCROLL_MODE: { | |
| 289 float x = g_viscosity_constants.Get().ApplyViscosity(t); | |
| 290 | |
| 291 curr_x_ = start_x_ + x * delta_x_; | |
| 292 curr_y_ = start_y_ + x * delta_y_; | |
| 293 } break; | |
| 294 | |
| 295 case FLING_MODE: { | |
| 296 float distance_coef = 1.f; | |
| 297 float velocity_coef = 0.f; | |
| 298 g_spline_constants.Get().CalculateCoefficients( | |
| 299 t, &distance_coef, &velocity_coef); | |
| 300 | |
| 301 curr_velocity_ = velocity_coef * distance_ * duration_seconds_reciprocal_; | |
| 302 | |
| 303 curr_x_ = start_x_ + distance_coef * delta_x_; | |
| 304 curr_x_ = Clamped(curr_x_, min_x_, max_x_); | |
| 305 | |
| 306 curr_y_ = start_y_ + distance_coef * delta_y_; | |
| 307 curr_y_ = Clamped(curr_y_, min_y_, max_y_); | |
| 308 | |
| 309 if (ApproxEquals(curr_x_, final_x_) && ApproxEquals(curr_y_, final_y_)) { | |
| 310 finished_ = true; | |
| 311 } | |
| 312 } break; | |
| 313 } | |
| 314 | |
| 315 return true; | |
| 316 } | |
| 317 | |
| 318 void Scroller::ExtendDuration(base::TimeDelta extend) { | |
| 319 base::TimeDelta passed = GetTimePassed(); | |
| 320 duration_ = passed + extend; | |
| 321 duration_seconds_reciprocal_ = 1. / duration_.InSecondsF(); | |
| 322 finished_ = false; | |
| 323 } | |
| 324 | |
| 325 void Scroller::SetFinalX(float new_x) { | |
| 326 final_x_ = new_x; | |
| 327 finished_ = false; | |
| 328 RecomputeDeltas(); | |
| 329 } | |
| 330 | |
| 331 void Scroller::SetFinalY(float new_y) { | |
| 332 final_y_ = new_y; | |
| 333 finished_ = false; | |
| 334 RecomputeDeltas(); | |
| 335 } | |
| 336 | |
| 337 void Scroller::AbortAnimation() { | |
| 338 curr_x_ = final_x_; | |
| 339 curr_y_ = final_y_; | |
| 340 curr_time_ = start_time_ + duration_; | |
| 341 finished_ = true; | |
| 342 } | |
| 343 | |
| 344 void Scroller::ForceFinished(bool finished) { finished_ = finished; } | |
| 345 | |
| 346 bool Scroller::IsFinished() const { return finished_; } | |
| 347 | |
| 348 base::TimeDelta Scroller::GetTimePassed() const { | |
| 349 return curr_time_ - start_time_; | |
| 350 } | |
| 351 | |
| 352 base::TimeDelta Scroller::GetDuration() const { return duration_; } | |
| 353 | |
| 354 float Scroller::GetCurrX() const { return curr_x_; } | |
| 355 | |
| 356 float Scroller::GetCurrY() const { return curr_y_; } | |
| 357 | |
| 358 float Scroller::GetCurrVelocity() const { | |
| 359 return mode_ == FLING_MODE | |
| 360 ? curr_velocity_ | |
| 361 : velocity_ - deceleration_ * GetTimePassed().InSecondsF() * 0.5f; | |
| 362 } | |
| 363 | |
| 364 float Scroller::GetCurrVelocityX() const { | |
| 365 return delta_x_norm_ * GetCurrVelocity(); | |
| 366 } | |
| 367 | |
| 368 float Scroller::GetCurrVelocityY() const { | |
| 369 return delta_y_norm_ * GetCurrVelocity(); | |
| 370 } | |
| 371 | |
| 372 float Scroller::GetStartX() const { return start_x_; } | |
| 373 | |
| 374 float Scroller::GetStartY() const { return start_y_; } | |
| 375 | |
| 376 float Scroller::GetFinalX() const { return final_x_; } | |
| 377 | |
| 378 float Scroller::GetFinalY() const { return final_y_; } | |
| 379 | |
| 380 bool Scroller::IsScrollingInDirection(float xvel, float yvel) const { | |
| 381 return !finished_ && Signum(xvel) == Signum(delta_x_) && | |
| 382 Signum(yvel) == Signum(delta_y_); | |
| 383 } | |
| 384 | |
| 385 void Scroller::RecomputeDeltas() { | |
| 386 delta_x_ = final_x_ - start_x_; | |
| 387 delta_y_ = final_y_ - start_y_; | |
| 388 | |
| 389 const float hyp = std::sqrt(delta_x_ * delta_x_ + delta_y_ * delta_y_); | |
| 390 if (hyp > kEpsilon) { | |
| 391 delta_x_norm_ = delta_x_ / hyp; | |
| 392 delta_y_norm_ = delta_y_ / hyp; | |
| 393 } else { | |
| 394 delta_x_norm_ = delta_y_norm_ = 1; | |
| 395 } | |
| 396 } | |
| 397 | |
| 398 double Scroller::GetSplineDeceleration(float velocity) const { | |
| 399 return std::log(kInflexion * std::abs(velocity) / | |
| 400 (fling_friction_ * tuning_coeff_)); | |
| 401 } | |
| 402 | |
| 403 base::TimeDelta Scroller::GetSplineFlingDuration(float velocity) const { | |
| 404 const double l = GetSplineDeceleration(velocity); | |
| 405 const double decel_minus_one = kDecelerationRate - 1.0; | |
| 406 const double time_seconds = std::exp(l / decel_minus_one); | |
| 407 return base::TimeDelta::FromMicroseconds(time_seconds * | |
| 408 base::Time::kMicrosecondsPerSecond); | |
| 409 } | |
| 410 | |
| 411 double Scroller::GetSplineFlingDistance(float velocity) const { | |
| 412 const double l = GetSplineDeceleration(velocity); | |
| 413 const double decel_minus_one = kDecelerationRate - 1.0; | |
| 414 return fling_friction_ * tuning_coeff_ * | |
| 415 std::exp(kDecelerationRate / decel_minus_one * l); | |
| 416 } | |
| 417 | |
| 418 } // namespace gfx | |
| OLD | NEW |