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

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

Powered by Google App Engine
This is Rietveld 408576698