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

Side by Side Diff: content/browser/renderer_host/input/gestures/velocity_tracker.cc

Issue 128613003: [Tracking Patch] Unified gesture detection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 11 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 "content/browser/renderer_host/input/gestures/velocity_tracker.h"
6
7 #include <math.h>
8
9 #include "base/logging.h"
10 #include "content/browser/renderer_host/input/gestures/motion_event.h"
11
12 using base::TimeDelta;
13 using base::TimeTicks;
14
15 namespace content {
16 namespace {
17
18 // Threshold for determining that a pointer has stopped moving.
19 // Some input devices do not send ACTION_MOVE events in the case where a pointer
20 // hasstopped. We need to detect this case so that we can accurately predict
21 // the velocity after the pointer starts moving again.
22 const TimeDelta ASSUME_POINTER_STOPPED_TIME = TimeDelta::FromMilliseconds(40);
23
24 // The default velocity tracker strategy.
25 // Although other strategies are available for testing and comparison purposes,
26 // this is the strategy that applications will actually use. Be very careful
27 // when adjusting the default strategy because it can dramatically affect
28 // (often in a bad way) the user experience.
29 const char DEFAULT_STRATEGY[] = "lsq2";
30
31 static float VectorDot(const float* a, const float* b, uint32_t m) {
32 float r = 0;
33 while (m--) {
34 r += *(a++) * *(b++);
35 }
36 return r;
37 }
38
39 static float VectorNorm(const float* a, uint32_t m) {
40 float r = 0;
41 while (m--) {
42 float t = *(a++);
43 r += t * t;
44 }
45 return sqrtf(r);
46 }
47
48 struct Estimator {
49 enum { MAX_DEGREE = 4 };
50
51 // Estimator time base.
52 TimeTicks time;
53
54 // Polynomial coefficients describing motion in X and Y.
55 float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
56
57 // Polynomial degree (number of coefficients), or zero if no information is
58 // available.
59 uint32_t degree;
60
61 // Confidence (coefficient of determination), between 0 (no fit)
62 // and 1 (perfect fit).
63 float confidence;
64
65 inline void Clear() {
66 time = TimeTicks();
67 degree = 0;
68 confidence = 0;
69 for (size_t i = 0; i <= MAX_DEGREE; i++) {
70 xCoeff[i] = 0;
71 yCoeff[i] = 0;
72 }
73 }
74 };
75
76 // Velocity tracker algorithm based on least-squares linear regression.
77 class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
78 public:
79 enum Weighting {
80 // No weights applied. All data points are equally reliable.
81 WEIGHTING_NONE,
82
83 // Weight by time delta. Data points clustered together are weighted less.
84 WEIGHTING_DELTA,
85
86 // Weight such that points within a certain horizon are weighed more than
87 // those outside of that horizon.
88 WEIGHTING_CENTRAL,
89
90 // Weight such that points older than a certain amount are weighed less.
91 WEIGHTING_RECENT,
92 };
93
94 // Degree must be no greater than Estimator::MAX_DEGREE.
95 LeastSquaresVelocityTrackerStrategy(uint32_t degree,
96 Weighting weighting = WEIGHTING_NONE);
97 virtual ~LeastSquaresVelocityTrackerStrategy();
98
99 virtual void Clear() OVERRIDE;
100 virtual void ClearPointers(BitSet32 id_bits) OVERRIDE;
101 virtual void AddMovement(const TimeTicks& event_time,
102 BitSet32 id_bits,
103 const VelocityTracker::Position* positions) OVERRIDE;
104 virtual bool GetEstimator(uint32_t id,
105 Estimator* out_estimator) const OVERRIDE;
106
107 private:
108 // Sample horizon.
109 // We don't use too much history by default since we want to react to quick
110 // changes in direction.
111 static const TimeDelta HORIZON;
112
113 // Number of samples to keep.
114 enum { HISTORY_SIZE = 20 };
115
116 struct Movement {
117 TimeTicks event_time;
118 BitSet32 id_bits;
119 VelocityTracker::Position positions[VelocityTracker::MAX_POINTERS];
120
121 inline const VelocityTracker::Position& GetPosition(uint32_t id) const {
122 return positions[id_bits.get_index_of_bit(id)];
123 }
124 };
125
126 float ChooseWeight(uint32_t index) const;
127
128 const uint32_t mDegree;
129 const Weighting mWeighting;
130 uint32_t index_;
131 Movement movements_[HISTORY_SIZE];
132 };
133
134 // Velocity tracker algorithm that uses an IIR filter.
135 class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy {
136 public:
137 // Degree must be 1 or 2.
138 explicit IntegratingVelocityTrackerStrategy(uint32_t degree);
139 virtual ~IntegratingVelocityTrackerStrategy();
140
141 virtual void Clear() OVERRIDE;
142 virtual void ClearPointers(BitSet32 id_bits) OVERRIDE;
143 virtual void AddMovement(const TimeTicks& event_time,
144 BitSet32 id_bits,
145 const VelocityTracker::Position* positions) OVERRIDE;
146 virtual bool GetEstimator(uint32_t id,
147 Estimator* out_estimator) const OVERRIDE;
148
149 private:
150 // Current state estimate for a particular pointer.
151 struct State {
152 TimeTicks updateTime;
153 uint32_t degree;
154
155 float xpos, xvel, xaccel;
156 float ypos, yvel, yaccel;
157 };
158
159 const uint32_t mDegree;
160 BitSet32 pointer_id_bits_;
161 State mPointerState[VelocityTracker::MAX_POINTER_ID + 1];
162
163 void InitState(State& state,
164 const TimeTicks& event_time,
165 float xpos,
166 float ypos) const;
167 void UpdateState(State& state,
168 const TimeTicks& event_time,
169 float xpos,
170 float ypos) const;
171 void PopulateEstimator(const State& state, Estimator* out_estimator) const;
172 };
173
174 } // namespace
175
176 // --- VelocityTracker ---
177
178 VelocityTracker::VelocityTracker()
179 : current_pointer_id_bits_(0), active_pointer_id_(-1) {
180 if (!ConfigureStrategy(DEFAULT_STRATEGY))
181 NOTREACHED() << "Unrecognized velocity tracker strategy: "
182 << DEFAULT_STRATEGY;
183 }
184
185 VelocityTracker::VelocityTracker(const char* strategy)
186 : current_pointer_id_bits_(0), active_pointer_id_(-1) {
187 if (!strategy)
188 strategy = DEFAULT_STRATEGY;
189
190 if (!ConfigureStrategy(strategy))
191 NOTREACHED() << "Unrecognized velocity tracker strategy: " << strategy;
192 }
193
194 VelocityTracker::~VelocityTracker() {}
195
196 void VelocityTracker::Clear() {
197 current_pointer_id_bits_.clear();
198 active_pointer_id_ = -1;
199 strategy_->Clear();
200 }
201
202 void VelocityTracker::ClearPointers(BitSet32 id_bits) {
203 BitSet32 remaining_id_bits(current_pointer_id_bits_.value & ~id_bits.value);
204 current_pointer_id_bits_ = remaining_id_bits;
205
206 if (active_pointer_id_ >= 0 && id_bits.has_bit(active_pointer_id_)) {
207 active_pointer_id_ =
208 !remaining_id_bits.is_empty() ? remaining_id_bits.first_marked_bit()
209 : -1;
210 }
211
212 strategy_->ClearPointers(id_bits);
213 }
214
215 void VelocityTracker::AddMovement(const TimeTicks& event_time,
216 BitSet32 id_bits,
217 const Position* positions) {
218 while (id_bits.count() > MAX_POINTERS)
219 id_bits.clear_last_marked_bit();
220
221 if ((current_pointer_id_bits_.value & id_bits.value) &&
222 event_time >= last_event_time_ + ASSUME_POINTER_STOPPED_TIME) {
223 // We have not received any movements for too long. Assume that all
224 // pointers
225 // have stopped.
226 strategy_->Clear();
227 }
228 last_event_time_ = event_time;
229
230 current_pointer_id_bits_ = id_bits;
231 if (active_pointer_id_ < 0 || !id_bits.has_bit(active_pointer_id_))
232 active_pointer_id_ = id_bits.is_empty() ? -1 : id_bits.first_marked_bit();
233
234 strategy_->AddMovement(event_time, id_bits, positions);
235 }
236
237 void VelocityTracker::AddMovement(const MotionEvent& event) {
238 int32_t actionMasked = event.GetActionMasked();
239
240 switch (actionMasked) {
241 case MotionEvent::ACTION_DOWN:
242 // case MotionEvent::HOVER_ENTER:
243 // Clear all pointers on down before adding the new movement.
244 Clear();
245 break;
246 case MotionEvent::ACTION_POINTER_DOWN: {
247 // Start a new movement trace for a pointer that just went down.
248 // We do this on down instead of on up because the client may want to
249 // query the
250 // final velocity for a pointer that just went up.
251 BitSet32 downIdBits;
252 downIdBits.mark_bit(event.GetPointerId(event.GetActionIndex()));
253 ClearPointers(downIdBits);
254 break;
255 }
256 case MotionEvent::ACTION_MOVE:
257 // case AMOTION_EVENT_ACTION_HOVER_MOVE:
258 break;
259 default:
260 // Ignore all other actions because they do not convey any new information
261 // about
262 // pointer movement. We also want to preserve the last known velocity of
263 // the pointers.
264 // Note that ACTION_UP and ACTION_POINTER_UP always report the last known
265 // position
266 // of the pointers that went up. ACTION_POINTER_UP does include the new
267 // position of
268 // pointers that remained down but we will also receive an ACTION_MOVE
269 // with this
270 // information if any of them actually moved. Since we don't know how
271 // many pointers
272 // will be going up at once it makes sense to just wait for the following
273 // ACTION_MOVE
274 // before adding the movement.
275 return;
276 }
277
278 size_t pointerCount = event.GetPointerCount();
279 if (pointerCount > MAX_POINTERS)
280 pointerCount = MAX_POINTERS;
281
282 BitSet32 id_bits;
283 for (size_t i = 0; i < pointerCount; i++)
284 id_bits.mark_bit(event.GetPointerId(i));
285
286 uint32_t pointerIndex[MAX_POINTERS];
287 for (size_t i = 0; i < pointerCount; i++)
288 pointerIndex[i] = id_bits.get_index_of_bit(event.GetPointerId(i));
289
290 TimeTicks event_time;
291 Position positions[pointerCount];
292
293 size_t historySize = event.GetHistorySize();
294 for (size_t h = 0; h < historySize; h++) {
295 event_time = event.GetHistoricalEventTime(h);
296 for (size_t i = 0; i < pointerCount; i++) {
297 uint32_t index = pointerIndex[i];
298 positions[index].x = event.GetHistoricalX(i, h);
299 positions[index].y = event.GetHistoricalY(i, h);
300 }
301 AddMovement(event_time, id_bits, positions);
302 }
303
304 event_time = event.GetEventTime();
305 for (size_t i = 0; i < pointerCount; i++) {
306 uint32_t index = pointerIndex[i];
307 positions[index].x = event.GetX(i);
308 positions[index].y = event.GetY(i);
309 }
310 AddMovement(event_time, id_bits, positions);
311 }
312
313 bool VelocityTracker::GetVelocity(uint32_t id,
314 float* outVx,
315 float* outVy) const {
316 Estimator estimator;
317 if (GetEstimator(id, &estimator) && estimator.degree >= 1) {
318 *outVx = estimator.xCoeff[1];
319 *outVy = estimator.yCoeff[1];
320 return true;
321 }
322 *outVx = 0;
323 *outVy = 0;
324 return false;
325 }
326
327 void LeastSquaresVelocityTrackerStrategy::AddMovement(
328 const TimeTicks& event_time,
329 BitSet32 id_bits,
330 const VelocityTracker::Position* positions) {
331 if (++index_ == HISTORY_SIZE) {
332 index_ = 0;
333 }
334
335 Movement& movement = movements_[index_];
336 movement.event_time = event_time;
337 movement.id_bits = id_bits;
338 uint32_t count = id_bits.count();
339 for (uint32_t i = 0; i < count; i++) {
340 movement.positions[i] = positions[i];
341 }
342 }
343
344 bool VelocityTracker::GetEstimator(uint32_t id,
345 Estimator* out_estimator) const {
346 return strategy_->GetEstimator(id, out_estimator);
347 }
348
349
350 bool VelocityTracker::ConfigureStrategy(const char* strategy) {
351 strategy_.reset(CreateStrategy(strategy));
352 return !!strategy_;
353 }
354
355 VelocityTrackerStrategy* VelocityTracker::CreateStrategy(const char* strategy) {
356 if (!strcmp("lsq1", strategy)) {
357 // 1st order least squares. Quality: POOR.
358 // Frequently underfits the touch data especially when the finger
359 // accelerates
360 // or changes direction. Often underestimates velocity. The direction
361 // is overly influenced by historical touch points.
362 return new LeastSquaresVelocityTrackerStrategy(1);
363 }
364 if (!strcmp("lsq2", strategy)) {
365 // 2nd order least squares. Quality: VERY GOOD.
366 // Pretty much ideal, but can be confused by certain kinds of touch data,
367 // particularly if the panel has a tendency to generate delayed,
368 // duplicate or jittery touch coordinates when the finger is released.
369 return new LeastSquaresVelocityTrackerStrategy(2);
370 }
371 if (!strcmp("lsq3", strategy)) {
372 // 3rd order least squares. Quality: UNUSABLE.
373 // Frequently overfits the touch data yielding wildly divergent estimates
374 // of the velocity when the finger is released.
375 return new LeastSquaresVelocityTrackerStrategy(3);
376 }
377 if (!strcmp("wlsq2-delta", strategy)) {
378 // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
379 return new LeastSquaresVelocityTrackerStrategy(
380 2, LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
381 }
382 if (!strcmp("wlsq2-central", strategy)) {
383 // 2nd order weighted least squares, central weighting. Quality:
384 // EXPERIMENTAL
385 return new LeastSquaresVelocityTrackerStrategy(
386 2, LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
387 }
388 if (!strcmp("wlsq2-recent", strategy)) {
389 // 2nd order weighted least squares, recent weighting. Quality:
390 // EXPERIMENTAL
391 return new LeastSquaresVelocityTrackerStrategy(
392 2, LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
393 }
394 if (!strcmp("int1", strategy)) {
395 // 1st order integrating filter. Quality: GOOD.
396 // Not as good as 'lsq2' because it cannot estimate acceleration but it is
397 // more tolerant of errors. Like 'lsq1', this strategy tends to
398 // underestimate
399 // the velocity of a fling but this strategy tends to respond to changes in
400 // direction more quickly and accurately.
401 return new IntegratingVelocityTrackerStrategy(1);
402 }
403 if (!strcmp("int2", strategy)) {
404 // 2nd order integrating filter. Quality: EXPERIMENTAL.
405 // For comparison purposes only. Unlike 'int1' this strategy can compensate
406 // for acceleration but it typically overestimates the effect.
407 return new IntegratingVelocityTrackerStrategy(2);
408 }
409 return NULL;
410 }
411
412 // --- LeastSquaresVelocityTrackerStrategy ---
413
414 const TimeDelta LeastSquaresVelocityTrackerStrategy::HORIZON =
415 TimeDelta::FromMilliseconds(100);
416
417 LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(
418 uint32_t degree,
419 Weighting weighting)
420 : mDegree(degree), mWeighting(weighting) {
421 Clear();
422 }
423
424 LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() {}
425
426 void LeastSquaresVelocityTrackerStrategy::Clear() {
427 index_ = 0;
428 movements_[0].id_bits.clear();
429 }
430
431 /**
432 * Solves a linear least squares problem to obtain a N degree polynomial that
433 * fits the specified input data as nearly as possible.
434 *
435 * Returns true if a solution is found, false otherwise.
436 *
437 * The input consists of two vectors of data points X and Y with indices 0..m-1
438 * along with a weight vector W of the same size.
439 *
440 * The output is a vector B with indices 0..n that describes a polynomial
441 * that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1]
442 * X[i] * + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is
443 * minimized.
444 *
445 * Accordingly, the weight vector W should be initialized by the caller with the
446 * reciprocal square root of the variance of the error in each input data point.
447 * In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 /
448 * stddev(Y[i]).
449 * The weights express the relative importance of each data point. If the
450 * weights are* all 1, then the data points are considered to be of equal
451 * importance when fitting the polynomial. It is a good idea to choose weights
452 * that diminish the importance of data points that may have higher than usual
453 * error margins.
454 *
455 * Errors among data points are assumed to be independent. W is represented
456 * here as a vector although in the literature it is typically taken to be a
457 * diagonal matrix.
458 *
459 * That is to say, the function that generated the input data can be
460 * approximated by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
461 *
462 * The coefficient of determination (R^2) is also returned to describe the
463 * goodness of fit of the model for the given data. It is a value between 0
464 * and 1, where 1 indicates perfect correspondence.
465 *
466 * This function first expands the X vector to a m by n matrix A such that
467 * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then
468 * multiplies it by w[i]./
469 *
470 * Then it calculates the QR decomposition of A yielding an m by m orthonormal
471 * matrix Q and an m by n upper triangular matrix R. Because R is upper
472 * triangular (lower part is all zeroes), we can simplify the decomposition into
473 * an m by n matrix Q1 and a n by n matrix R1 such that A = Q1 R1.
474 *
475 * Finally we solve the system of linear equations given by
476 * R1 B = (Qtranspose W Y) to find B.
477 *
478 * For efficiency, we lay out A and Q column-wise in memory because we
479 * frequently operate on the column vectors. Conversely, we lay out R row-wise.
480 *
481 * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
482 * http://en.wikipedia.org/wiki/Gram-Schmidt
483 */
484 static bool SolveLeastSquares(const float* x,
485 const float* y,
486 const float* w,
487 uint32_t m,
488 uint32_t n,
489 float* outB,
490 float* outDet) {
491 // Expand the X vector to a matrix A, pre-multiplied by the weights.
492 float a[n][m]; // column-major order
493 for (uint32_t h = 0; h < m; h++) {
494 a[0][h] = w[h];
495 for (uint32_t i = 1; i < n; i++) {
496 a[i][h] = a[i - 1][h] * x[h];
497 }
498 }
499
500 // Apply the Gram-Schmidt process to A to obtain its QR decomposition.
501 float q[n][m]; // orthonormal basis, column-major order
502 float r[n][n]; // upper triangular matrix, row-major order
503 for (uint32_t j = 0; j < n; j++) {
504 for (uint32_t h = 0; h < m; h++) {
505 q[j][h] = a[j][h];
506 }
507 for (uint32_t i = 0; i < j; i++) {
508 float dot = VectorDot(&q[j][0], &q[i][0], m);
509 for (uint32_t h = 0; h < m; h++) {
510 q[j][h] -= dot * q[i][h];
511 }
512 }
513
514 float norm = VectorNorm(&q[j][0], m);
515 if (norm < 0.000001f) {
516 // vectors are linearly dependent or zero so no solution
517 return false;
518 }
519
520 float invNorm = 1.0f / norm;
521 for (uint32_t h = 0; h < m; h++) {
522 q[j][h] *= invNorm;
523 }
524 for (uint32_t i = 0; i < n; i++) {
525 r[j][i] = i < j ? 0 : VectorDot(&q[j][0], &a[i][0], m);
526 }
527 }
528
529 // Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
530 // We just work from bottom-right to top-left calculating B's coefficients.
531 float wy[m];
532 for (uint32_t h = 0; h < m; h++) {
533 wy[h] = y[h] * w[h];
534 }
535 for (uint32_t i = n; i-- != 0;) {
536 outB[i] = VectorDot(&q[i][0], wy, m);
537 for (uint32_t j = n - 1; j > i; j--) {
538 outB[i] -= r[i][j] * outB[j];
539 }
540 outB[i] /= r[i][i];
541 }
542
543 // Calculate the coefficient of determination as 1 - (SSerr / SStot) where
544 // SSerr is the residual sum of squares (variance of the error),
545 // and SStot is the total sum of squares (variance of the data) where each
546 // has been weighted.
547 float ymean = 0;
548 for (uint32_t h = 0; h < m; h++) {
549 ymean += y[h];
550 }
551 ymean /= m;
552
553 float sserr = 0;
554 float sstot = 0;
555 for (uint32_t h = 0; h < m; h++) {
556 float err = y[h] - outB[0];
557 float term = 1;
558 for (uint32_t i = 1; i < n; i++) {
559 term *= x[h];
560 err -= term * outB[i];
561 }
562 sserr += w[h] * w[h] * err * err;
563 float var = y[h] - ymean;
564 sstot += w[h] * w[h] * var * var;
565 }
566 *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
567 return true;
568 }
569
570 void LeastSquaresVelocityTrackerStrategy::ClearPointers(BitSet32 id_bits) {
571 BitSet32 remaining_id_bits(movements_[index_].id_bits.value & ~id_bits.value);
572 movements_[index_].id_bits = remaining_id_bits;
573 }
574
575 bool LeastSquaresVelocityTrackerStrategy::GetEstimator(
576 uint32_t id,
577 Estimator* out_estimator) const {
578 out_estimator->Clear();
579
580 // Iterate over movement samples in reverse time order and collect samples.
581 float x[HISTORY_SIZE];
582 float y[HISTORY_SIZE];
583 float w[HISTORY_SIZE];
584 float time[HISTORY_SIZE];
585 uint32_t m = 0;
586 uint32_t index = index_;
587 const Movement& newestMovement = movements_[index_];
588 do {
589 const Movement& movement = movements_[index];
590 if (!movement.id_bits.has_bit(id))
591 break;
592
593 TimeDelta age = newestMovement.event_time - movement.event_time;
594 if (age > HORIZON)
595 break;
596
597 const VelocityTracker::Position& position = movement.GetPosition(id);
598 x[m] = position.x;
599 y[m] = position.y;
600 w[m] = ChooseWeight(index);
601 time[m] = -static_cast<float>(age.InSecondsF());
602 index = (index == 0 ? HISTORY_SIZE : index) - 1;
603 } while (++m < HISTORY_SIZE);
604
605 if (m == 0)
606 return false; // no data
607
608 // Calculate a least squares polynomial fit.
609 uint32_t degree = mDegree;
610 if (degree > m - 1)
611 degree = m - 1;
612
613 if (degree >= 1) {
614 float xdet, ydet;
615 uint32_t n = degree + 1;
616 if (SolveLeastSquares(time, x, w, m, n, out_estimator->xCoeff, &xdet) &&
617 SolveLeastSquares(time, y, w, m, n, out_estimator->yCoeff, &ydet)) {
618 out_estimator->time = newestMovement.event_time;
619 out_estimator->degree = degree;
620 out_estimator->confidence = xdet * ydet;
621 return true;
622 }
623 }
624
625 // No velocity data available for this pointer, but we do have its current
626 // position.
627 out_estimator->xCoeff[0] = x[0];
628 out_estimator->yCoeff[0] = y[0];
629 out_estimator->time = newestMovement.event_time;
630 out_estimator->degree = 0;
631 out_estimator->confidence = 1;
632 return true;
633 }
634
635 float LeastSquaresVelocityTrackerStrategy::ChooseWeight(uint32_t index) const {
636 switch (mWeighting) {
637 case WEIGHTING_DELTA: {
638 // Weight points based on how much time elapsed between them and the next
639 // point so that points that "cover" a shorter time span are weighed less.
640 // delta 0ms: 0.5
641 // delta 10ms: 1.0
642 if (index == index_) {
643 return 1.0f;
644 }
645 uint32_t nextIndex = (index + 1) % HISTORY_SIZE;
646 float delta_millis = static_cast<float>(
647 (movements_[nextIndex].event_time - movements_[index].event_time)
648 .InMillisecondsF());
649 if (delta_millis < 0)
650 return 0.5f;
651 if (delta_millis < 10)
652 return 0.5f + delta_millis * 0.05;
653
654 return 1.0f;
655 }
656
657 case WEIGHTING_CENTRAL: {
658 // Weight points based on their age, weighing very recent and very old
659 // points less.
660 // age 0ms: 0.5
661 // age 10ms: 1.0
662 // age 50ms: 1.0
663 // age 60ms: 0.5
664 float age_millis = static_cast<float>(
665 (movements_[index_].event_time - movements_[index].event_time)
666 .InMillisecondsF());
667 if (age_millis < 0)
668 return 0.5f;
669 if (age_millis < 10)
670 return 0.5f + age_millis * 0.05;
671 if (age_millis < 50)
672 return 1.0f;
673 if (age_millis < 60)
674 return 0.5f + (60 - age_millis) * 0.05;
675
676 return 0.5f;
677 }
678
679 case WEIGHTING_RECENT: {
680 // Weight points based on their age, weighing older points less.
681 // age 0ms: 1.0
682 // age 50ms: 1.0
683 // age 100ms: 0.5
684 float age_millis = static_cast<float>(
685 (movements_[index_].event_time - movements_[index].event_time)
686 .InMillisecondsF());
687 if (age_millis < 50) {
688 return 1.0f;
689 }
690 if (age_millis < 100) {
691 return 0.5f + (100 - age_millis) * 0.01f;
692 }
693 return 0.5f;
694 }
695
696 case WEIGHTING_NONE:
697 default:
698 return 1.0f;
699 }
700 }
701
702 // --- IntegratingVelocityTrackerStrategy ---
703
704 IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(
705 uint32_t degree)
706 : mDegree(degree) {}
707
708 IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() {}
709
710 void IntegratingVelocityTrackerStrategy::Clear() { pointer_id_bits_.clear(); }
711
712 void IntegratingVelocityTrackerStrategy::ClearPointers(BitSet32 id_bits) {
713 pointer_id_bits_.value &= ~id_bits.value;
714 }
715
716 void IntegratingVelocityTrackerStrategy::AddMovement(
717 const TimeTicks& event_time,
718 BitSet32 id_bits,
719 const VelocityTracker::Position* positions) {
720 uint32_t index = 0;
721 for (BitSet32 iter_id_bits(id_bits); !iter_id_bits.is_empty();) {
722 uint32_t id = iter_id_bits.clear_first_marked_bit();
723 State& state = mPointerState[id];
724 const VelocityTracker::Position& position = positions[index++];
725 if (pointer_id_bits_.has_bit(id))
726 UpdateState(state, event_time, position.x, position.y);
727 else
728 InitState(state, event_time, position.x, position.y);
729 }
730
731 pointer_id_bits_ = id_bits;
732 }
733
734 bool IntegratingVelocityTrackerStrategy::GetEstimator(
735 uint32_t id,
736 Estimator* out_estimator) const {
737 out_estimator->Clear();
738
739 if (pointer_id_bits_.has_bit(id)) {
740 const State& state = mPointerState[id];
741 PopulateEstimator(state, out_estimator);
742 return true;
743 }
744
745 return false;
746 }
747
748 void IntegratingVelocityTrackerStrategy::InitState(
749 State& state,
750 const TimeTicks& event_time,
751 float xpos,
752 float ypos) const {
753 state.updateTime = event_time;
754 state.degree = 0;
755
756 state.xpos = xpos;
757 state.xvel = 0;
758 state.xaccel = 0;
759 state.ypos = ypos;
760 state.yvel = 0;
761 state.yaccel = 0;
762 }
763
764 void IntegratingVelocityTrackerStrategy::UpdateState(
765 State& state,
766 const TimeTicks& event_time,
767 float xpos,
768 float ypos) const {
769 const base::TimeDelta MIN_TIME_DELTA = TimeDelta::FromMicroseconds(2);
770 const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds
771
772 if (event_time <= state.updateTime + MIN_TIME_DELTA)
773 return;
774
775 float dt = static_cast<float>((event_time - state.updateTime).InSecondsF());
776 state.updateTime = event_time;
777
778 float xvel = (xpos - state.xpos) / dt;
779 float yvel = (ypos - state.ypos) / dt;
780 if (state.degree == 0) {
781 state.xvel = xvel;
782 state.yvel = yvel;
783 state.degree = 1;
784 } else {
785 float alpha = dt / (FILTER_TIME_CONSTANT + dt);
786 if (mDegree == 1) {
787 state.xvel += (xvel - state.xvel) * alpha;
788 state.yvel += (yvel - state.yvel) * alpha;
789 } else {
790 float xaccel = (xvel - state.xvel) / dt;
791 float yaccel = (yvel - state.yvel) / dt;
792 if (state.degree == 1) {
793 state.xaccel = xaccel;
794 state.yaccel = yaccel;
795 state.degree = 2;
796 } else {
797 state.xaccel += (xaccel - state.xaccel) * alpha;
798 state.yaccel += (yaccel - state.yaccel) * alpha;
799 }
800 state.xvel += (state.xaccel * dt) * alpha;
801 state.yvel += (state.yaccel * dt) * alpha;
802 }
803 }
804 state.xpos = xpos;
805 state.ypos = ypos;
806 }
807
808 void IntegratingVelocityTrackerStrategy::PopulateEstimator(
809 const State& state,
810 Estimator* out_estimator) const {
811 out_estimator->time = state.updateTime;
812 out_estimator->confidence = 1.0f;
813 out_estimator->degree = state.degree;
814 out_estimator->xCoeff[0] = state.xpos;
815 out_estimator->xCoeff[1] = state.xvel;
816 out_estimator->xCoeff[2] = state.xaccel / 2;
817 out_estimator->yCoeff[0] = state.ypos;
818 out_estimator->yCoeff[1] = state.yvel;
819 out_estimator->yCoeff[2] = state.yaccel / 2;
820 }
821
822 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698