Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(25)

Side by Side Diff: ui/gfx/android/scroller.cc

Issue 172933004: [Android] Port Scroller.java to C++ and use for fling animations (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698